From 6cecb044c1dcd2dfd889b59df0a2846be55335b9 Mon Sep 17 00:00:00 2001 From: hongruichen Date: Sun, 8 Sep 2024 23:15:22 +0800 Subject: [PATCH] try fix h616 thermal sensors --- ...Add-thermal-sensor-and-thermal-zones.patch | 18 +- ...v-thermal-sun8i_thermal-Add-for-H616.patch | 19 +- .../driver-allwinner-h618-emac.patch | 4516 ----------------- ...dd-pwm-sunxi-enhance-driver-for-h616.patch | 1313 ----- .../kernel/archive/sunxi-6.10/series.armbian | 2 + patch/kernel/archive/sunxi-6.10/series.conf | 2 + patch/kernel/archive/sunxi-6.10/series.megous | 10 - 7 files changed, 26 insertions(+), 5854 deletions(-) rename patch/kernel/archive/sunxi-6.10/{patches.megous => patches.armbian}/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch (90%) rename patch/kernel/archive/sunxi-6.10/{patches.megous => patches.armbian}/drv-thermal-sun8i_thermal-Add-for-H616.patch (89%) delete mode 100644 patch/kernel/archive/sunxi-6.10/patches.megous/driver-allwinner-h618-emac.patch delete mode 100644 patch/kernel/archive/sunxi-6.10/patches.megous/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch diff --git a/patch/kernel/archive/sunxi-6.10/patches.megous/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch b/patch/kernel/archive/sunxi-6.10/patches.armbian/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch similarity index 90% rename from patch/kernel/archive/sunxi-6.10/patches.megous/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch rename to patch/kernel/archive/sunxi-6.10/patches.armbian/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch index 4ce124d8e2ad..36a287343546 100644 --- a/patch/kernel/archive/sunxi-6.10/patches.megous/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch +++ b/patch/kernel/archive/sunxi-6.10/patches.armbian/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch @@ -11,10 +11,10 @@ Signed-off-by: Kali Prasad 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -index 921d5f61d8d6..35ec95ed63f6 100644 +index 747964647578..1bf71a0a0202 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -@@ -26,6 +26,7 @@ cpu0: cpu@0 { +@@ -29,6 +29,7 @@ cpu0: cpu@0 { reg = <0>; enable-method = "psci"; clocks = <&ccu CLK_CPUX>; @@ -22,7 +22,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 #cooling-cells = <2>; }; -@@ -35,6 +36,7 @@ cpu1: cpu@1 { +@@ -38,6 +39,7 @@ cpu1: cpu@1 { reg = <1>; enable-method = "psci"; clocks = <&ccu CLK_CPUX>; @@ -30,7 +30,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 #cooling-cells = <2>; }; -@@ -44,6 +46,7 @@ cpu2: cpu@2 { +@@ -47,6 +49,7 @@ cpu2: cpu@2 { reg = <2>; enable-method = "psci"; clocks = <&ccu CLK_CPUX>; @@ -38,7 +38,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 #cooling-cells = <2>; }; -@@ -53,6 +56,7 @@ cpu3: cpu@3 { +@@ -56,6 +59,7 @@ cpu3: cpu@3 { reg = <3>; enable-method = "psci"; clocks = <&ccu CLK_CPUX>; @@ -46,7 +46,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 #cooling-cells = <2>; }; }; -@@ -864,24 +868,37 @@ cpu-thermal { +@@ -1209,24 +1213,37 @@ cpu-thermal { polling-delay = <1000>; thermal-sensors = <&ths 2>; sustainable-power = <1000>; @@ -87,7 +87,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 }; gpu-thermal { -@@ -891,7 +908,7 @@ gpu-thermal { +@@ -1236,7 +1253,7 @@ gpu-thermal { sustainable-power = <1100>; trips { @@ -96,7 +96,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 temperature = <110000>; type = "critical"; hysteresis = <0>; -@@ -905,7 +922,7 @@ ve-thermal { +@@ -1250,7 +1267,7 @@ ve-thermal { thermal-sensors = <&ths 1>; trips { @@ -105,7 +105,7 @@ index 921d5f61d8d6..35ec95ed63f6 100644 temperature = <110000>; type = "critical"; hysteresis = <0>; -@@ -919,7 +936,7 @@ ddr-thermal { +@@ -1264,7 +1281,7 @@ ddr-thermal { thermal-sensors = <&ths 3>; trips { diff --git a/patch/kernel/archive/sunxi-6.10/patches.megous/drv-thermal-sun8i_thermal-Add-for-H616.patch b/patch/kernel/archive/sunxi-6.10/patches.armbian/drv-thermal-sun8i_thermal-Add-for-H616.patch similarity index 89% rename from patch/kernel/archive/sunxi-6.10/patches.megous/drv-thermal-sun8i_thermal-Add-for-H616.patch rename to patch/kernel/archive/sunxi-6.10/patches.armbian/drv-thermal-sun8i_thermal-Add-for-H616.patch index ee38fe937441..5470042661ad 100644 --- a/patch/kernel/archive/sunxi-6.10/patches.megous/drv-thermal-sun8i_thermal-Add-for-H616.patch +++ b/patch/kernel/archive/sunxi-6.10/patches.armbian/drv-thermal-sun8i_thermal-Add-for-H616.patch @@ -8,11 +8,11 @@ two additional temperature sensors for VE and DDR. Signed-off-by: Kali Prasad --- - drivers/thermal/sun8i_thermal.c | 94 +++++++++++++++++++++++++++++++-- - 1 file changed, 91 insertions(+), 3 deletions(-) + drivers/thermal/sun8i_thermal.c | 99 +++++++++++++++++++++++++++++++-- + 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c -index c6a82ef84aca..c1dc688eb449 100644 +index c6a82ef84aca..362a4e7fa517 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -112,6 +112,12 @@ static int sun50i_h5_calc_temp(struct ths_device *tmdev, @@ -124,9 +124,16 @@ index c6a82ef84aca..c1dc688eb449 100644 static int sun8i_ths_register(struct ths_device *tmdev) { -@@ -720,10 +808,10 @@ static const struct ths_thermal_chip sun50i_h616_ths = { - .offset = 263655, - .scale = 810, +@@ -715,15 +803,14 @@ static const struct ths_thermal_chip sun20i_d1_ths = { + static const struct ths_thermal_chip sun50i_h616_ths = { + .sensor_num = 4, + .has_bus_clk_reset = true, +- .needs_sram = true, + .ft_deviation = 8000, +- .offset = 263655, +- .scale = 810, ++ .offset = -3255, ++ .scale = -81, .temp_data_base = SUN50I_H6_THS_TEMP_DATA, - .calibrate = sun50i_h6_ths_calibrate, - .init = sun50i_h6_thermal_init, diff --git a/patch/kernel/archive/sunxi-6.10/patches.megous/driver-allwinner-h618-emac.patch b/patch/kernel/archive/sunxi-6.10/patches.megous/driver-allwinner-h618-emac.patch deleted file mode 100644 index 1fb1d794eeef..000000000000 --- a/patch/kernel/archive/sunxi-6.10/patches.megous/driver-allwinner-h618-emac.patch +++ /dev/null @@ -1,4516 +0,0 @@ -From e4f6baf0e8d317b22daff4a2a5667950a717bd6a Mon Sep 17 00:00:00 2001 -From: chraac -Date: Fri, 16 Aug 2024 16:44:41 +0800 -Subject: driver: allwinner h618 emac - ---- - drivers/gpio/gpiolib-of.c | 29 +- - drivers/mfd/Kconfig | 10 + - drivers/mfd/Makefile | 1 + - drivers/mfd/sunxi-ac200.c | 288 +++ - drivers/net/ethernet/allwinner/Kconfig | 8 + - drivers/net/ethernet/allwinner/Makefile | 2 + - drivers/net/ethernet/allwinner/sunxi-gmac.c | 2219 +++++++++++++++++ - drivers/net/ethernet/allwinner/sunxi-gmac.h | 270 ++ - .../net/ethernet/allwinner/sunxi_gmac_ops.c | 768 ++++++ - drivers/net/phy/Kconfig | 8 + - drivers/net/phy/Makefile | 1 + - drivers/net/phy/sunxi-ephy.c | 518 ++++ - include/linux/mfd/ac200.h | 213 ++ - include/linux/of_gpio.h | 18 + - 14 files changed, 4338 insertions(+), 15 deletions(-) - create mode 100644 drivers/mfd/sunxi-ac200.c - create mode 100644 drivers/net/ethernet/allwinner/sunxi-gmac.c - create mode 100644 drivers/net/ethernet/allwinner/sunxi-gmac.h - create mode 100644 drivers/net/ethernet/allwinner/sunxi_gmac_ops.c - create mode 100644 drivers/net/phy/sunxi-ephy.c - create mode 100644 include/linux/mfd/ac200.h - -diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c -index 89d5e64cf68b..fc3ac57bb67d 100644 ---- a/drivers/gpio/gpiolib-of.c -+++ b/drivers/gpio/gpiolib-of.c -@@ -25,21 +25,6 @@ - #include "gpiolib.h" - #include "gpiolib-of.h" - --/* -- * This is Linux-specific flags. By default controllers' and Linux' mapping -- * match, but GPIO controllers are free to translate their own flags to -- * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. -- */ --enum of_gpio_flags { -- OF_GPIO_ACTIVE_LOW = 0x1, -- OF_GPIO_SINGLE_ENDED = 0x2, -- OF_GPIO_OPEN_DRAIN = 0x4, -- OF_GPIO_TRANSITORY = 0x8, -- OF_GPIO_PULL_UP = 0x10, -- OF_GPIO_PULL_DOWN = 0x20, -- OF_GPIO_PULL_DISABLE = 0x40, --}; -- - /** - * of_gpio_named_count() - Count GPIOs for a device - * @np: device node to count GPIOs for -@@ -427,6 +412,20 @@ static struct gpio_desc *of_get_named_gpiod_flags(const struct device_node *np, - return desc; - } - -+int of_get_named_gpio_flags(const struct device_node *np, const char *list_name, -+ int index, enum of_gpio_flags *flags) -+{ -+ struct gpio_desc *desc; -+ -+ desc = of_get_named_gpiod_flags(np, list_name, index, flags); -+ -+ if (IS_ERR(desc)) -+ return PTR_ERR(desc); -+ else -+ return desc_to_gpio(desc); -+} -+EXPORT_SYMBOL_GPL(of_get_named_gpio_flags); -+ - /** - * of_get_named_gpio() - Get a GPIO number to use with GPIO API - * @np: device node to get GPIO from -diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig -index 266b4f54af60..3b81f50f95e6 100644 ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -191,6 +191,16 @@ config MFD_AC100 - This driver include only the core APIs. You have to select individual - components like codecs or RTC under the corresponding menus. - -+config MFD_AC200_SUNXI -+ tristate "X-Powers AC200 (Sunxi)" -+ select MFD_CORE -+ depends on I2C -+ depends on PWM_SUNXI_ENHANCE -+ help -+ If you say Y here you get support for the X-Powers AC200 IC. -+ This driver include only the core APIs. You have to select individual -+ components like Ethernet PHY or RTC under the corresponding menus. -+ - config MFD_AXP20X - tristate - select MFD_CORE -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index db1ba39de3b5..f1fd50749112 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -140,6 +140,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o - obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o - - obj-$(CONFIG_MFD_AC100) += ac100.o -+obj-$(CONFIG_MFD_AC200_SUNXI) += sunxi-ac200.o - obj-$(CONFIG_MFD_AXP20X) += axp20x.o - obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o - obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o -diff --git a/drivers/mfd/sunxi-ac200.c b/drivers/mfd/sunxi-ac200.c -new file mode 100644 -index 000000000000..7c5b0e0523cf ---- /dev/null -+++ b/drivers/mfd/sunxi-ac200.c -@@ -0,0 +1,288 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * MFD core driver for X-Powers' AC200 IC -+ * -+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and -+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse -+ * and RTC. -+ * -+ * Copyright (c) 2020 Jernej Skrabec -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SUNXI_AC300_KEY (0x1 << 8) -+ -+/* Interrupts */ -+#define AC200_IRQ_RTC 0 -+#define AC200_IRQ_EPHY 1 -+#define AC200_IRQ_TVE 2 -+ -+/* IRQ enable register */ -+#define AC200_SYS_IRQ_ENABLE_OUT_EN BIT(15) -+#define AC200_SYS_IRQ_ENABLE_RTC BIT(12) -+#define AC200_SYS_IRQ_ENABLE_EPHY BIT(8) -+#define AC200_SYS_IRQ_ENABLE_TVE BIT(4) -+ -+static const struct regmap_range_cfg ac200_range_cfg[] = { -+ { -+ .range_min = AC200_SYS_VERSION, -+ .range_max = AC200_IC_CHARA1, -+ .selector_reg = AC200_TWI_REG_ADDR_H, -+ .selector_mask = 0xff, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 256, -+ } -+}; -+ -+static const struct regmap_config ac200_regmap_config = { -+ .name = "ac200", -+ .reg_bits = 8, -+ .val_bits = 16, -+ .ranges = ac200_range_cfg, -+ .num_ranges = ARRAY_SIZE(ac200_range_cfg), -+ .max_register = AC200_IC_CHARA1, -+}; -+ -+static const struct regmap_irq ac200_regmap_irqs[] = { -+ REGMAP_IRQ_REG(AC200_IRQ_RTC, 0, AC200_SYS_IRQ_ENABLE_RTC), -+ REGMAP_IRQ_REG(AC200_IRQ_EPHY, 0, AC200_SYS_IRQ_ENABLE_EPHY), -+ REGMAP_IRQ_REG(AC200_IRQ_TVE, 0, AC200_SYS_IRQ_ENABLE_TVE), -+}; -+ -+static const struct regmap_irq_chip ac200_regmap_irq_chip = { -+ .name = "ac200_irq_chip", -+ .status_base = AC200_SYS_IRQ_STATUS, -+ .mask_base = AC200_SYS_IRQ_ENABLE, -+ .irqs = ac200_regmap_irqs, -+ .num_irqs = ARRAY_SIZE(ac200_regmap_irqs), -+ .num_regs = 1, -+}; -+ -+static const struct resource ephy_resource[] = { -+ DEFINE_RES_IRQ(AC200_IRQ_EPHY), -+}; -+ -+static const struct mfd_cell ac200_cells[] = { -+ { -+ .name = "ac200-ephy-sunxi", -+ .num_resources = ARRAY_SIZE(ephy_resource), -+ .resources = ephy_resource, -+ .of_compatible = "x-powers,ac200-ephy-sunxi", -+ }, -+ { -+ .name = "acx00-codec", -+ .of_compatible = "x-powers,ac200-codec-sunxi", -+ }, -+}; -+ -+atomic_t ac200_en; -+ -+int ac200_enable(void) -+{ -+ return atomic_read(&ac200_en); -+} -+ -+EXPORT_SYMBOL(ac200_enable); -+ -+static uint16_t ephy_caldata = 0; -+ -+static int sun50i_ephy_get_calibrate(struct device *dev) -+{ -+ struct nvmem_cell *calcell; -+ uint16_t *caldata; -+ size_t callen; -+ int ret = 0; -+ -+ calcell = devm_nvmem_cell_get(dev, "calibration"); -+ if (IS_ERR(calcell)) { -+ dev_err_probe(dev, PTR_ERR(calcell), -+ "Failed to get calibration nvmem cell (%pe)\n", -+ calcell); -+ -+ if (PTR_ERR(calcell) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ goto out; -+ } -+ -+ caldata = nvmem_cell_read(calcell, &callen); -+ if (IS_ERR(caldata)) { -+ ret = PTR_ERR(caldata); -+ dev_err(dev, "Failed to read calibration data (%pe)\n", -+ caldata); -+ goto out; -+ } -+ -+ ephy_caldata = *caldata; -+ kfree(caldata); -+out: -+ return ret; -+} -+ -+uint16_t sun50i_ephy_calibrate_value(void) -+{ -+ return ephy_caldata; -+} -+ -+EXPORT_SYMBOL(sun50i_ephy_calibrate_value); -+ -+static int ac200_i2c_probe(struct i2c_client *i2c) -+{ -+ struct device *dev = &i2c->dev; -+ struct ac200_dev *ac200; -+ uint32_t ephy_cal; -+ int ret; -+ -+ // 24Mhz clock for both ac200 and ac300 devices -+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); -+ if (!ac200) -+ return -ENOMEM; -+ -+ ac200->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(ac200->clk)) { -+ dev_err(dev, "Can't obtain the clock!\n"); -+ return PTR_ERR(ac200->clk); -+ } -+ -+ ret = clk_prepare_enable(ac200->clk); -+ if (ret) { -+ dev_err(dev, "rclk_prepare_enable failed! \n"); -+ return ret; -+ } -+ -+ ret = sun50i_ephy_get_calibrate(dev); -+ if (ret) { -+ dev_err(dev, "sun50i get ephy id failed\n"); -+ return ret; -+ } -+ ephy_cal = sun50i_ephy_calibrate_value(); -+ -+ if (ephy_cal & SUNXI_AC300_KEY) { -+ pr_warn("it's ac300, skip the ac200 init!\n"); -+ return -EINVAL; -+ } else { -+ pr_warn("it's ac200, ac200 init start!\n"); -+ } -+ -+ i2c_set_clientdata(i2c, ac200); -+ -+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); -+ if (IS_ERR(ac200->regmap)) -+ { -+ ret = PTR_ERR(ac200->regmap); -+ dev_err(dev, "regmap init failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* do a reset to put chip in a known state */ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ if (ret) -+ { -+ dev_err(dev, "AC200_SYS_CONTROL 0 failed! \n"); -+ return ret; -+ } -+ atomic_set(&ac200_en, 0); -+ -+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); -+ if (ret) -+ { -+ dev_err(dev, "AC200_SYS_CONTROL 1 failed! \n"); -+ return ret; -+ } -+ atomic_set(&ac200_en, 1); -+ -+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, -+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); -+ if (ret) -+ { -+ dev_err(dev, "failed to add MFD devices: %d\n", ret); -+ return ret; -+ } -+ else -+ { -+ dev_err(dev, "add MFD devices success! \n"); -+ } -+ -+ return 0; -+} -+ -+static void ac200_i2c_remove(struct i2c_client *i2c) -+{ -+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); -+ -+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+ -+ mfd_remove_devices(&i2c->dev); -+}static void ac200_i2c_shutdown(struct i2c_client *i2c) -+{ -+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); -+ -+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0); -+} -+ -+static int ac200_i2c_suspend(struct device *dev) -+{ -+ struct ac200_dev *ac200 = dev_get_drvdata(dev); -+ -+ if (!IS_ERR_OR_NULL(ac200->clk)) -+ clk_disable_unprepare(ac200->clk); -+ -+ atomic_set(&ac200_en, 0); -+ return 0; -+} -+ -+static int ac200_i2c_resume(struct device *dev) -+{ -+ struct ac200_dev *ac200 = dev_get_drvdata(dev); -+ -+ if (!IS_ERR_OR_NULL(ac200->clk)) -+ clk_prepare_enable(ac200->clk); -+ -+ atomic_set(&ac200_en, 0); -+ msleep(40); -+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1); -+ atomic_set(&ac200_en, 1); -+ return 0; -+} -+ -+static const struct dev_pm_ops ac200_core_pm_ops = { -+ .suspend_late = ac200_i2c_suspend, -+ .resume_early = ac200_i2c_resume, -+}; -+ -+static const struct i2c_device_id ac200_ids[] = { -+ { "ac200", }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(i2c, ac200_ids); -+ -+static const struct of_device_id ac200_of_match[] = { -+ { .compatible = "x-powers,ac200-sunxi" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, ac200_of_match); -+ -+static struct i2c_driver ac200_i2c_driver = { -+ .driver = { -+ .name = "ac200-sunxi", -+ .of_match_table = of_match_ptr(ac200_of_match), -+ .pm = &ac200_core_pm_ops, -+ }, -+ .probe = ac200_i2c_probe, -+ .remove = ac200_i2c_remove, -+ .shutdown = ac200_i2c_shutdown, -+ .id_table = ac200_ids, -+}; -+module_i2c_driver(ac200_i2c_driver); -+ -+MODULE_DESCRIPTION("MFD core driver for AC200"); -+MODULE_AUTHOR("Jernej Skrabec "); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig -index 3e81059f8693..36d808810ca4 100644 ---- a/drivers/net/ethernet/allwinner/Kconfig -+++ b/drivers/net/ethernet/allwinner/Kconfig -@@ -34,4 +34,12 @@ config SUN4I_EMAC - To compile this driver as a module, choose M here. The module - will be called sun4i-emac. - -+config SUNXI_GMAC -+ tristate "Allwinner GMAC support" -+ depends on ARCH_SUNXI -+ depends on OF -+ depends on AC200_PHY_SUNXI -+ select CRC32 -+ select MII -+ - endif # NET_VENDOR_ALLWINNER -diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile -index ddd5a5079e8a..56b9c434a5b8 100644 ---- a/drivers/net/ethernet/allwinner/Makefile -+++ b/drivers/net/ethernet/allwinner/Makefile -@@ -4,3 +4,5 @@ - # - - obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o -+sunxi_gmac-objs := sunxi_gmac_ops.o sunxi-gmac.o -+obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o -diff --git a/drivers/net/ethernet/allwinner/sunxi-gmac.c b/drivers/net/ethernet/allwinner/sunxi-gmac.c -new file mode 100644 -index 000000000000..30efda8038f2 ---- /dev/null -+++ b/drivers/net/ethernet/allwinner/sunxi-gmac.c -@@ -0,0 +1,2219 @@ -+/* -+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c -+ * -+ * Copyright © 2016-2018, fuzhaoke -+ * Author: fuzhaoke -+ * -+ * This file is provided under a dual BSD/GPL license. When using or -+ * redistributing this file, you may do so under either 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "sunxi-gmac.h" -+#include -+#include -+#include -+ -+ -+int sunxi_get_soc_chipid(unsigned char *chipid); -+ -+#define DMA_DESC_RX 256 -+#define DMA_DESC_TX 256 -+#define BUDGET (dma_desc_rx / 4) -+#define TX_THRESH (dma_desc_tx / 4) -+ -+#define HASH_TABLE_SIZE 64 -+#define MAX_BUF_SZ (SZ_2K - 1) -+ -+#define POWER_CHAN_NUM 3 -+ -+#undef PKT_DEBUG -+#undef DESC_PRINT -+ -+#define circ_cnt(head, tail, size) (((head) > (tail)) ? ((head) - (tail)) : ((head) - (tail)) & ((size) - 1)) -+ -+#define circ_space(head, tail, size) circ_cnt((tail), ((head) + 1), (size)) -+ -+#define circ_inc(n, s) (((n) + 1) % (s)) -+ -+#define GETH_MAC_ADDRESS "01:02:03:04:05:06" -+static char *mac_str = GETH_MAC_ADDRESS; -+module_param(mac_str, charp, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(mac_str, "MAC Address String.(xx:xx:xx:xx:xx:xx)"); -+ -+static int rxmode = 1; -+module_param(rxmode, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(rxmode, "DMA threshold control value"); -+ -+static int txmode = 1; -+module_param(txmode, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(txmode, "DMA threshold control value"); -+ -+static int pause = 0x400; -+module_param(pause, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(pause, "Flow Control Pause Time"); -+ -+#define TX_TIMEO 5000 -+static int watchdog = TX_TIMEO; -+module_param(watchdog, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds"); -+ -+static int dma_desc_rx = DMA_DESC_RX; -+module_param(dma_desc_rx, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(watchdog, "The number of receive's descriptors"); -+ -+static int dma_desc_tx = DMA_DESC_TX; -+module_param(dma_desc_tx, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(watchdog, "The number of transmit's descriptors"); -+ -+/* - 0: Flow Off -+ * - 1: Rx Flow -+ * - 2: Tx Flow -+ * - 3: Rx & Tx Flow -+ */ -+static int flow_ctrl; -+module_param(flow_ctrl, int, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(flow_ctrl, "Flow control [0: off, 1: rx, 2: tx, 3: both]"); -+ -+static unsigned long tx_delay; -+module_param(tx_delay, ulong, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(tx_delay, "Adjust transmit clock delay, value: 0~7"); -+ -+static unsigned long rx_delay; -+module_param(rx_delay, ulong, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(rx_delay, "Adjust receive clock delay, value: 0~31"); -+ -+/* whether using ephy_clk */ -+static int g_use_ephy_clk; -+static int g_phy_addr; -+ -+struct geth_priv { -+ struct dma_desc *dma_tx; -+ struct sk_buff **tx_sk; -+ unsigned int tx_clean; -+ unsigned int tx_dirty; -+ dma_addr_t dma_tx_phy; -+ -+ struct reset_control *ephy_rst; -+ -+ unsigned long buf_sz; -+ -+ struct dma_desc *dma_rx; -+ struct sk_buff **rx_sk; -+ unsigned int rx_clean; -+ unsigned int rx_dirty; -+ dma_addr_t dma_rx_phy; -+ -+ struct net_device *ndev; -+ struct device *dev; -+ struct napi_struct napi; -+ -+ struct geth_extra_stats xstats; -+ -+ struct mii_bus *mii; -+ int link; -+ int speed; -+ int duplex; -+#define INT_PHY 0 -+#define EXT_PHY 1 -+ int phy_ext; -+ int phy_interface; -+ -+ void __iomem *base; -+ void __iomem *base_phy; -+ struct clk *geth_clk; -+ struct clk *ephy_clk; -+ struct pinctrl *pinctrl; -+ -+ struct regulator *gmac_power[POWER_CHAN_NUM]; -+ bool is_suspend; -+ int phyrst; -+ u8 rst_active_low; -+ /* definition spinlock */ -+ spinlock_t lock; -+ spinlock_t tx_lock; -+ -+ /* resume work */ -+ struct work_struct eth_work; -+}; -+ -+static u64 geth_dma_mask = DMA_BIT_MASK(32); -+ -+void sunxi_udelay(int n) -+{ -+ udelay(n); -+} -+ -+static int geth_stop(struct net_device *ndev); -+static int geth_open(struct net_device *ndev); -+static void geth_tx_complete(struct geth_priv *priv); -+static void geth_rx_refill(struct net_device *ndev); -+ -+#ifdef CONFIG_GETH_ATTRS -+static ssize_t adjust_bgs_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ int value = 0; -+ u32 efuse_value; -+ struct net_device *ndev = to_net_dev(dev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ if (priv->phy_ext == INT_PHY) { -+ value = readl(priv->base_phy) >> 28; -+ if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0) -+ pr_err("get PHY efuse fail!\n"); -+ else -+#if defined(CONFIG_ARCH_SUN50IW2) -+ value = value - ((efuse_value >> 24) & 0x0F); -+#else -+ pr_warn("miss config come from efuse!\n"); -+#endif -+ } -+ -+ return sprintf(buf, "bgs: %d\n", value); -+} -+ -+static ssize_t adjust_bgs_write(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ unsigned int out = 0; -+ struct net_device *ndev = to_net_dev(dev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ u32 clk_value = readl(priv->base_phy); -+ u32 efuse_value; -+ -+ out = simple_strtoul(buf, NULL, 10); -+ -+ if (priv->phy_ext == INT_PHY) { -+ clk_value &= ~(0xF << 28); -+ if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0) -+ pr_err("get PHY efuse fail!\n"); -+ else -+#if defined(CONFIG_ARCH_SUN50IW2) -+ clk_value |= (((efuse_value >> 24) & 0x0F) + out) << 28; -+#else -+ pr_warn("miss config come from efuse!\n"); -+#endif -+ } -+ -+ writel(clk_value, priv->base_phy); -+ -+ return count; -+} -+ -+static struct device_attribute adjust_reg[] = { -+ __ATTR(adjust_bgs, 0664, adjust_bgs_show, adjust_bgs_write), -+}; -+ -+static int geth_create_attrs(struct net_device *ndev) -+{ -+ int j, ret; -+ -+ for (j = 0; j < ARRAY_SIZE(adjust_reg); j++) { -+ ret = device_create_file(&ndev->dev, &adjust_reg[j]); -+ if (ret) -+ goto sysfs_failed; -+ } -+ goto succeed; -+ -+sysfs_failed: -+ while (j--) -+ device_remove_file(&ndev->dev, &adjust_reg[j]); -+succeed: -+ return ret; -+} -+#endif -+ -+#ifdef DEBUG -+static void desc_print(struct dma_desc *desc, int size) -+{ -+#ifdef DESC_PRINT -+ int i; -+ -+ for (i = 0; i < size; i++) { -+ u32 *x = (u32 *)(desc + i); -+ -+ pr_info("\t%d [0x%08lx]: %08x %08x %08x %08x\n", -+ i, (unsigned long)(&desc[i]), -+ x[0], x[1], x[2], x[3]); -+ } -+ pr_info("\n"); -+#endif -+} -+#endif -+ -+static ssize_t gphy_test_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct net_device *ndev = dev_get_drvdata(dev); -+ -+ if (!dev) { -+ pr_err("Argment is invalid\n"); -+ return 0; -+ } -+ -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return 0; -+ } -+ -+ return sprintf(buf, "Usage:\necho [0/1/2/3/4] > gphy_test\n" -+ "0 - Normal Mode\n" -+ "1 - Transmit Jitter Test\n" -+ "2 - Transmit Jitter Test(MASTER mode)\n" -+ "3 - Transmit Jitter Test(SLAVE mode)\n" -+ "4 - Transmit Distortion Test\n\n"); -+} -+ -+static ssize_t gphy_test_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct net_device *ndev = dev_get_drvdata(dev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ u16 value = 0; -+ int ret = 0; -+ u16 data = 0; -+ -+ if (!dev) { -+ pr_err("Argument is invalid\n"); -+ return count; -+ } -+ -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return count; -+ } -+ -+ data = sunxi_mdio_read(priv->base, g_phy_addr, MII_CTRL1000); -+ -+ ret = kstrtou16(buf, 0, &value); -+ if (ret) -+ return ret; -+ -+ if (value >= 0 && value <= 4) { -+ data &= ~(0x7 << 13); -+ data |= value << 13; -+ sunxi_mdio_write(priv->base, g_phy_addr, MII_CTRL1000, data); -+ pr_info("Set MII_CTRL1000(0x09) Reg: 0x%x\n", data); -+ } else { -+ pr_info("unknown value (%d)\n", value); -+ } -+ -+ return count; -+} -+ -+static DEVICE_ATTR(gphy_test, 0664, gphy_test_show, gphy_test_store); -+ -+static struct mii_reg_dump mii_reg; -+ -+static ssize_t mii_read_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct net_device *ndev; -+ struct geth_priv *priv; -+ -+ if (!dev) { -+ pr_err("Argment is invalid\n"); -+ return 0; -+ } -+ -+ ndev = dev_get_drvdata(dev); -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return 0; -+ } -+ -+ priv = netdev_priv(ndev); -+ if (!priv) { -+ pr_err("geth_priv is null\n"); -+ return 0; -+ } -+ -+ if (!netif_running(ndev)) { -+ pr_warn("eth is down!\n"); -+ return 0; -+ } -+ -+ mii_reg.value = sunxi_mdio_read(priv->base, mii_reg.addr, mii_reg.reg); -+ return sprintf(buf, "ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n", -+ mii_reg.addr, mii_reg.reg, mii_reg.value); -+} -+ -+static ssize_t mii_read_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct net_device *ndev; -+ struct geth_priv *priv; -+ int ret = 0; -+ u16 reg, addr; -+ char *ptr; -+ -+ ptr = (char *)buf; -+ -+ if (!dev) { -+ pr_err("Argment is invalid\n"); -+ return count; -+ } -+ -+ ndev = dev_get_drvdata(dev); -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return count; -+ } -+ -+ priv = netdev_priv(ndev); -+ if (!priv) { -+ pr_err("geth_priv is null\n"); -+ return count; -+ } -+ -+ if (!netif_running(ndev)) { -+ pr_warn("eth is down!\n"); -+ return count; -+ } -+ -+ ret = sunxi_parse_read_str(ptr, &addr, ®); -+ if (ret) -+ return ret; -+ -+ mii_reg.addr = addr; -+ mii_reg.reg = reg; -+ -+ return count; -+} -+ -+static DEVICE_ATTR(mii_read, 0664, mii_read_show, mii_read_store); -+ -+static ssize_t mii_write_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct net_device *ndev; -+ struct geth_priv *priv; -+ u16 bef_val, aft_val; -+ -+ if (!dev) { -+ pr_err("Argment is invalid\n"); -+ return 0; -+ } -+ -+ ndev = dev_get_drvdata(dev); -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return 0; -+ } -+ -+ priv = netdev_priv(ndev); -+ if (!priv) { -+ pr_err("geth_priv is null\n"); -+ return 0; -+ } -+ -+ if (!netif_running(ndev)) { -+ pr_warn("eth is down!\n"); -+ return 0; -+ } -+ -+ bef_val = sunxi_mdio_read(priv->base, mii_reg.addr, mii_reg.reg); -+ sunxi_mdio_write(priv->base, mii_reg.addr, mii_reg.reg, mii_reg.value); -+ aft_val = sunxi_mdio_read(priv->base, mii_reg.addr, mii_reg.reg); -+ return sprintf(buf, "before ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n" -+ "after ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n", -+ mii_reg.addr, mii_reg.reg, bef_val, -+ mii_reg.addr, mii_reg.reg, aft_val); -+} -+ -+static ssize_t mii_write_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct net_device *ndev; -+ struct geth_priv *priv; -+ int ret = 0; -+ u16 reg, addr, val; -+ char *ptr; -+ -+ ptr = (char *)buf; -+ -+ if (!dev) { -+ pr_err("Argment is invalid\n"); -+ return count; -+ } -+ -+ ndev = dev_get_drvdata(dev); -+ if (!ndev) { -+ pr_err("Net device is null\n"); -+ return count; -+ } -+ -+ priv = netdev_priv(ndev); -+ if (!priv) { -+ pr_err("geth_priv is null\n"); -+ return count; -+ } -+ -+ if (!netif_running(ndev)) { -+ pr_warn("eth is down!\n"); -+ return count; -+ } -+ -+ ret = sunxi_parse_write_str(ptr, &addr, ®, &val); -+ if (ret) -+ return ret; -+ -+ mii_reg.reg = reg; -+ mii_reg.addr = addr; -+ mii_reg.value = val; -+ -+ return count; -+} -+ -+static DEVICE_ATTR(mii_write, 0664, mii_write_show, mii_write_store); -+ -+static int geth_power_on(struct geth_priv *priv) -+{ -+ int value; -+ int i; -+ -+ value = readl(priv->base_phy); -+ if (priv->phy_ext == INT_PHY) { -+ value |= (1 << 15); -+ value &= ~(1 << 16); -+ value |= (3 << 17); -+ } else { -+ value &= ~(1 << 15); -+ -+ for (i = 0; i < POWER_CHAN_NUM; i++) { -+ if (IS_ERR_OR_NULL(priv->gmac_power[i])) -+ continue; -+ if (regulator_enable(priv->gmac_power[i]) != 0) { -+ pr_err("gmac-power%d enable error\n", i); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ writel(value, priv->base_phy); -+ -+ return 0; -+} -+ -+static void geth_power_off(struct geth_priv *priv) -+{ -+ int value; -+ int i; -+ -+ if (priv->phy_ext == INT_PHY) { -+ value = readl(priv->base_phy); -+ value |= (1 << 16); -+ writel(value, priv->base_phy); -+ } else { -+ for (i = 0; i < POWER_CHAN_NUM; i++) { -+ if (IS_ERR_OR_NULL(priv->gmac_power[i])) -+ continue; -+ regulator_disable(priv->gmac_power[i]); -+ } -+ } -+} -+ -+/* PHY interface operations */ -+static int geth_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) -+{ -+ struct net_device *ndev = bus->priv; -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ return (int)sunxi_mdio_read(priv->base, phyaddr, phyreg); -+} -+ -+static int geth_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 data) -+{ -+ struct net_device *ndev = bus->priv; -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ sunxi_mdio_write(priv->base, phyaddr, phyreg, data); -+ -+ return 0; -+} -+ -+static int geth_mdio_reset(struct mii_bus *bus) -+{ -+ struct net_device *ndev = bus->priv; -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ return sunxi_mdio_reset(priv->base); -+} -+ -+static void geth_adjust_link(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct phy_device *phydev = ndev->phydev; -+ unsigned long flags; -+ int new_state = 0; -+ -+ if (!phydev) -+ return; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ if (phydev->link) { -+ /* Now we make sure that we can be in full duplex mode. -+ * If not, we operate in half-duplex mode. -+ */ -+ if (phydev->duplex != priv->duplex) { -+ new_state = 1; -+ priv->duplex = phydev->duplex; -+ } -+ /* Flow Control operation */ -+ if (phydev->pause) -+ sunxi_flow_ctrl(priv->base, phydev->duplex, flow_ctrl, pause); -+ -+ if (phydev->speed != priv->speed) { -+ new_state = 1; -+ priv->speed = phydev->speed; -+ } -+ -+ if (priv->link == 0) { -+ new_state = 1; -+ priv->link = phydev->link; -+ } -+ -+ if (new_state) -+ sunxi_set_link_mode(priv->base, priv->duplex, priv->speed); -+ -+#ifdef LOOPBACK_DEBUG -+ phydev->state = PHY_FORCING; -+#endif -+ -+ } else if (priv->link != phydev->link) { -+ new_state = 1; -+ priv->link = 0; -+ priv->speed = 0; -+ priv->duplex = -1; -+ } -+ -+ if (new_state) -+ phy_print_status(phydev); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+ -+static int geth_phy_init(struct net_device *ndev) -+{ -+ int value; -+ struct mii_bus *new_bus; -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct phy_device *phydev = ndev->phydev; -+ u32 supported = 0, advertising = 0; -+ -+ /* Fixup the phy interface type */ -+ if (priv->phy_ext == INT_PHY) { -+ priv->phy_interface = PHY_INTERFACE_MODE_MII; -+ } else { -+ /* If config gpio to reset the phy device, we should reset it */ -+ if (gpio_is_valid(priv->phyrst)) { -+ gpio_direction_output(priv->phyrst, priv->rst_active_low); -+ msleep(50); -+ gpio_direction_output(priv->phyrst, !priv->rst_active_low); -+ msleep(50); -+ } -+ } -+ -+ new_bus = mdiobus_alloc(); -+ if (!new_bus) { -+ netdev_err(ndev, "Failed to alloc new mdio bus\n"); -+ return -ENOMEM; -+ } -+ -+ new_bus->name = dev_name(priv->dev); -+ new_bus->read = &geth_mdio_read; -+ new_bus->write = &geth_mdio_write; -+ new_bus->reset = &geth_mdio_reset; -+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0); -+ -+ new_bus->parent = priv->dev; -+ new_bus->priv = ndev; -+ -+ if (mdiobus_register(new_bus)) { -+ pr_err("%s: Cannot register as MDIO bus\n", new_bus->name); -+ goto reg_fail; -+ } -+ -+ priv->mii = new_bus; -+ -+ { -+ int addr; -+ for (addr = 0; addr < PHY_MAX_ADDR; addr++) { -+ struct phy_device *phydev_tmp = mdiobus_get_phy(new_bus, addr); -+ -+ if (IS_ERR_OR_NULL(phydev_tmp) || phydev_tmp->phy_id == 0xffff) { -+ if (!IS_ERR_OR_NULL(phydev_tmp)) -+ phy_device_remove(phydev_tmp); -+ phydev_tmp = mdiobus_scan_c22(new_bus, addr); -+ } -+ -+ if (IS_ERR_OR_NULL(phydev_tmp) || phydev_tmp->phy_id == 0xffff || -+ phydev_tmp->phy_id == 0x00 || phydev_tmp->phy_id == AC300_ID) -+ continue; -+ -+ if (phydev_tmp->phy_id == EPHY_ID || phydev_tmp->phy_id == IP101G_ID) { -+ phydev = phydev_tmp; -+ g_phy_addr = addr; -+ break; -+ } -+ } -+ } -+ -+ if (!phydev) { -+ netdev_err(ndev, "No PHY found!\n"); -+ goto err; -+ } -+ -+ phy_write(phydev, MII_BMCR, BMCR_RESET); -+ while (BMCR_RESET & phy_read(phydev, MII_BMCR)) -+ msleep(30); -+ -+ value = phy_read(phydev, MII_BMCR); -+ phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN)); -+ -+ phydev->irq = PHY_POLL; -+ -+ value = phy_connect_direct(ndev, phydev, &geth_adjust_link, priv->phy_interface); -+ if (value) { -+ netdev_err(ndev, "Could not attach to PHY\n"); -+ goto err; -+ } else { -+ netdev_info(ndev, "%s: Type(%d) PHY ID %08x at %d IRQ %s (%s)\n", -+ ndev->name, phydev->interface, phydev->phy_id, -+ phydev->mdio.addr, "poll", dev_name(&phydev->mdio.dev)); -+ } -+ -+#if 0 -+ phydev->supported &= PHY_GBIT_FEATURES; //kandy 000002cf -+ phydev->advertising = phydev->supported; -+#else -+ -+ ethtool_convert_link_mode_to_legacy_u32(&supported, phydev->supported); -+ -+ advertising = supported &= *PHY_BASIC_FEATURES; // kandy 000002cf -+ -+ ethtool_convert_legacy_u32_to_link_mode(phydev->supported, supported); -+ ethtool_convert_legacy_u32_to_link_mode(phydev->advertising, advertising); -+#endif -+ -+ if (priv->phy_ext == INT_PHY) { -+ /* EPHY Initial */ -+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ -+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ -+ phy_write(phydev, 0x1f, 0x0200); /* switchto page 2 */ -+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ -+ phy_write(phydev, 0x1f, 0x0600); /* switchto page 6 */ -+ phy_write(phydev, 0x14, 0x708F); /* PHYAFE TX optimization */ -+ phy_write(phydev, 0x19, 0x0000); -+ phy_write(phydev, 0x13, 0xf000); /* PHYAFE RX optimization */ -+ phy_write(phydev, 0x15, 0x1530); -+ phy_write(phydev, 0x1f, 0x0800); /* switch to page 8 */ -+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ -+ phy_write(phydev, 0x1f, 0x0100); /* switchto page 1 */ -+ /* reg 0x17 bit3,set 0 to disable iEEE */ -+ phy_write(phydev, 0x17, phy_read(phydev, 0x17) & (~(1<<3))); -+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ -+ } -+ -+ return 0; -+ -+err: -+ mdiobus_unregister(new_bus); -+reg_fail: -+ mdiobus_free(new_bus); -+ -+ return -EINVAL; -+} -+ -+static int geth_phy_release(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct phy_device *phydev = ndev->phydev; -+ int value = 0; -+ -+ /* Stop and disconnect the PHY */ -+ if (phydev) -+ phy_stop(phydev); -+ -+ priv->link = PHY_DOWN; -+ priv->speed = 0; -+ priv->duplex = -1; -+ -+ if (phydev) { -+ value = phy_read(phydev, MII_BMCR); -+ phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN)); -+ phy_disconnect(phydev); -+ ndev->phydev = NULL; -+ } -+ -+#if 1 // defined(CONFIG_SUNXI_EPHY) -+ gmac_ephy_shutdown(); /* Turn off the clock of PHY for low power consumption */ -+#endif -+ -+ if (priv->mii) { -+ mdiobus_unregister(priv->mii); -+ priv->mii->priv = NULL; -+ mdiobus_free(priv->mii); -+ priv->mii = NULL; -+ } -+ -+ return 0; -+} -+ -+static void geth_rx_refill(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct dma_desc *desc; -+ struct sk_buff *sk = NULL; -+ dma_addr_t paddr; -+ -+ while (circ_space(priv->rx_clean, priv->rx_dirty, dma_desc_rx) > 0) { -+ int entry = priv->rx_clean; -+ -+ /* Find the dirty's desc and clean it */ -+ desc = priv->dma_rx + entry; -+ -+ if (priv->rx_sk[entry] == NULL) { -+ sk = netdev_alloc_skb_ip_align(ndev, priv->buf_sz); -+ -+ if (unlikely(sk == NULL)) -+ break; -+ -+ priv->rx_sk[entry] = sk; -+ paddr = dma_map_single(priv->dev, sk->data, -+ priv->buf_sz, DMA_FROM_DEVICE); -+ desc_buf_set(desc, paddr, priv->buf_sz); -+ } -+ -+ /* sync memery */ -+ wmb(); -+ desc_set_own(desc); -+ priv->rx_clean = circ_inc(priv->rx_clean, dma_desc_rx); -+ } -+} -+ -+/* geth_dma_desc_init - initialize the RX/TX descriptor list -+ * @ndev: net device structure -+ * Description: initialize the list for dma. -+ */ -+static int geth_dma_desc_init(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ unsigned int buf_sz; -+ -+ priv->rx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_rx, GFP_KERNEL); -+ if (!priv->rx_sk) -+ return -ENOMEM; -+ -+ priv->tx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_tx, GFP_KERNEL); -+ if (!priv->tx_sk) -+ goto tx_sk_err; -+ -+ /* Set the size of buffer depend on the MTU & max buf size */ -+ buf_sz = MAX_BUF_SZ; -+ -+ priv->dma_tx = dma_alloc_coherent(priv->dev, -+ dma_desc_tx * sizeof(struct dma_desc), -+ &priv->dma_tx_phy, -+ GFP_KERNEL); -+ if (!priv->dma_tx) -+ goto dma_tx_err; -+ -+ priv->dma_rx = dma_alloc_coherent(priv->dev, -+ dma_desc_rx * sizeof(struct dma_desc), -+ &priv->dma_rx_phy, -+ GFP_KERNEL); -+ if (!priv->dma_rx) -+ goto dma_rx_err; -+ -+ priv->buf_sz = buf_sz; -+ -+ return 0; -+ -+dma_rx_err: -+ dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc), priv->dma_tx, priv->dma_tx_phy); -+dma_tx_err: -+ kfree(priv->tx_sk); -+tx_sk_err: -+ kfree(priv->rx_sk); -+ -+ return -ENOMEM; -+} -+ -+static void geth_free_rx_sk(struct geth_priv *priv) -+{ -+ int i; -+ -+ for (i = 0; i < dma_desc_rx; i++) { -+ if (priv->rx_sk[i] != NULL) { -+ struct dma_desc *desc = priv->dma_rx + i; -+ -+ dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc), desc_buf_get_len(desc), DMA_FROM_DEVICE); -+ dev_kfree_skb_any(priv->rx_sk[i]); -+ priv->rx_sk[i] = NULL; -+ } -+ } -+} -+ -+static void geth_free_tx_sk(struct geth_priv *priv) -+{ -+ int i; -+ -+ for (i = 0; i < dma_desc_tx; i++) { -+ if (priv->tx_sk[i] != NULL) { -+ struct dma_desc *desc = priv->dma_tx + i; -+ -+ if (desc_buf_get_addr(desc)) -+ dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc), desc_buf_get_len(desc), DMA_TO_DEVICE); -+ dev_kfree_skb_any(priv->tx_sk[i]); -+ priv->tx_sk[i] = NULL; -+ } -+ } -+} -+ -+static void geth_free_dma_desc(struct geth_priv *priv) -+{ -+ /* Free the region of consistent memory previously allocated for the DMA */ -+ dma_free_coherent(priv->dev, dma_desc_tx * sizeof(struct dma_desc), priv->dma_tx, priv->dma_tx_phy); -+ dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc), priv->dma_rx, priv->dma_rx_phy); -+ -+ kfree(priv->rx_sk); -+ kfree(priv->tx_sk); -+} -+ -+#ifdef CONFIG_PM -+static int geth_select_gpio_state(struct pinctrl *pctrl, char *name) -+{ -+ int ret = 0; -+ struct pinctrl_state *pctrl_state = NULL; -+ -+ pctrl_state = pinctrl_lookup_state(pctrl, name); -+ if (IS_ERR(pctrl_state)) { -+ pr_err("gmac pinctrl_lookup_state(%s) failed! return %p\n", name, pctrl_state); -+ return -EINVAL; -+ } -+ -+ ret = pinctrl_select_state(pctrl, pctrl_state); -+ if (ret < 0) -+ pr_err("gmac pinctrl_select_state(%s) failed! return %d\n", name, ret); -+ -+ return ret; -+} -+ -+static int geth_suspend(struct device *dev) -+{ -+ struct net_device *ndev = dev_get_drvdata(dev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ cancel_work_sync(&priv->eth_work); -+ -+ if (!ndev || !netif_running(ndev)) -+ return 0; -+ -+ spin_lock(&priv->lock); -+ netif_device_detach(ndev); -+ spin_unlock(&priv->lock); -+ -+ geth_stop(ndev); -+ -+ if (priv->phy_ext == EXT_PHY) -+ geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP); -+ -+ return 0; -+} -+ -+static void geth_resume_work(struct work_struct *work) -+{ -+ struct geth_priv *priv = container_of(work, struct geth_priv, eth_work); -+ struct net_device *ndev = priv->ndev; -+ -+ if (!netif_running(ndev)) -+ return; -+ -+ if (priv->phy_ext == EXT_PHY) -+ geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_DEFAULT); -+ -+ geth_open(ndev); -+ -+#if 1 //defined(CONFIG_SUNXI_EPHY) -+ if (!ephy_is_enable()) { -+ pr_info("[geth_resume] ephy is not enable, waiting...\n"); -+ msleep(2000); -+ if (!ephy_is_enable()) { -+ netdev_err(ndev, "Wait for ephy resume timeout.\n"); -+ return; -+ } -+ } -+#endif -+ -+ spin_lock(&priv->lock); -+ netif_device_attach(ndev); -+ spin_unlock(&priv->lock); -+ -+} -+ -+static void geth_resume(struct device *dev) -+{ -+ struct net_device *ndev = dev_get_drvdata(dev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ schedule_work(&priv->eth_work); -+} -+ -+ -+ -+static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) -+{ -+ struct net_device *ndev = platform_get_drvdata(ofdev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ cancel_work_sync(&priv->eth_work); -+ -+ if (!ndev || !netif_running(ndev)) -+ return 0; -+ -+ spin_lock(&priv->lock); -+ netif_device_detach(ndev); -+ spin_unlock(&priv->lock); -+ -+ geth_stop(ndev); -+ -+ if (priv->phy_ext == EXT_PHY) -+ geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP); -+ -+ -+ return 0; -+} -+ -+static int ucc_geth_resume(struct platform_device *ofdev) -+{ -+ struct net_device *ndev = platform_get_drvdata(ofdev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ schedule_work(&priv->eth_work); -+ -+ -+ return 0; -+} -+ -+static int geth_freeze(struct device *dev) -+{ -+ return 0; -+} -+ -+static int geth_restore(struct device *dev) -+{ -+ return 0; -+} -+ -+static const struct dev_pm_ops geth_pm_ops = { -+ .complete = geth_resume, -+ .prepare = geth_suspend, -+ .suspend = NULL, -+ .resume = NULL, -+ .freeze = geth_freeze, -+ .restore = geth_restore, -+}; -+#else -+static const struct dev_pm_ops geth_pm_ops; -+#endif /* CONFIG_PM */ -+ -+/*#define sunxi_get_soc_chipid(x) {}*/ -+static void geth_chip_hwaddr(u8 *addr) -+{ -+#define MD5_SIZE 16 -+#define CHIP_SIZE 16 -+ -+ struct crypto_ahash *tfm; -+ struct ahash_request *req; -+ struct scatterlist sg; -+ u8 result[MD5_SIZE]; -+ u8 chipid[CHIP_SIZE]; -+ int i = 0; -+ int ret = -1; -+ -+ memset(chipid, 0, sizeof(chipid)); -+ memset(result, 0, sizeof(result)); -+ -+ sunxi_get_soc_chipid((u8 *)chipid); -+ -+ tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC); -+ if (IS_ERR(tfm)) { -+ pr_err("Failed to alloc md5\n"); -+ return; -+ } -+ -+ req = ahash_request_alloc(tfm, GFP_KERNEL); -+ if (!req) -+ goto out; -+ -+ ahash_request_set_callback(req, 0, NULL, NULL); -+ -+ ret = crypto_ahash_init(req); -+ if (ret) { -+ pr_err("crypto_ahash_init() failed\n"); -+ goto out; -+ } -+ -+ sg_init_one(&sg, chipid, sizeof(chipid)); -+ ahash_request_set_crypt(req, &sg, result, sizeof(chipid)); -+ ret = crypto_ahash_update(req); -+ if (ret) { -+ pr_err("crypto_ahash_update() failed for id\n"); -+ goto out; -+ } -+ -+ ret = crypto_ahash_final(req); -+ if (ret) { -+ pr_err("crypto_ahash_final() failed for result\n"); -+ goto out; -+ } -+ -+ ahash_request_free(req); -+ -+ /* Choose md5 result's [0][2][4][6][8][10] byte as mac address */ -+ for (i = 0; i < ETH_ALEN; i++) -+ addr[i] = result[2 * i]; -+ addr[0] &= 0xfe; /* clear multicast bit */ -+ addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ -+ -+out: -+ crypto_free_ahash(tfm); -+} -+ -+static void geth_check_addr(struct net_device *ndev, unsigned char *mac) -+{ -+ int i; -+ char *p = mac; -+ u8 addr[ETH_ALEN]; -+ -+ if (!is_valid_ether_addr(ndev->dev_addr)) { -+ for (i = 0; i < ETH_ALEN; i++, p++) -+ addr[i] = simple_strtoul(p, &p, 16); -+ -+ if (!is_valid_ether_addr(addr)) -+ geth_chip_hwaddr(addr); -+ -+ if (!is_valid_ether_addr(addr)) { -+ eth_random_addr(addr); -+ pr_warn("%s: Use random mac address\n", ndev->name); -+ } -+ } -+ -+ eth_hw_addr_set(ndev, addr); -+} -+ -+static void geth_clk_enable(struct geth_priv *priv) -+{ -+ int phy_interface = 0; -+ u32 clk_value; -+ u32 efuse_value; -+ -+ if (clk_prepare_enable(priv->geth_clk)) -+ pr_err("try to enable geth_clk failed!\n"); -+ -+ if (((priv->phy_ext == INT_PHY) || g_use_ephy_clk) && !IS_ERR_OR_NULL(priv->ephy_clk)) { -+ if (clk_prepare_enable(priv->ephy_clk)) -+ pr_err("try to enable ephy_clk failed!\n"); -+ } -+ -+ phy_interface = priv->phy_interface; -+ -+ clk_value = readl(priv->base_phy); -+ if (phy_interface == PHY_INTERFACE_MODE_RGMII) -+ clk_value |= 0x00000004; -+ else -+ clk_value &= (~0x00000004); -+ -+ clk_value &= (~0x00002003); -+ if (phy_interface == PHY_INTERFACE_MODE_RGMII || phy_interface == PHY_INTERFACE_MODE_GMII) -+ clk_value |= 0x00000002; -+ else if (phy_interface == PHY_INTERFACE_MODE_RMII) -+ clk_value |= 0x00002001; -+ -+ if (priv->phy_ext == INT_PHY) { -+// if (0 != sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value)) -+// pr_err("get PHY efuse fail!\n"); -+// else -+// #if defined(CONFIG_ARCH_SUN50IW2) -+// clk_value |= (((efuse_value >> 24) & 0x0F) + 3) << 28; -+// #else -+// pr_warn("miss config come from efuse!\n"); -+// #endif -+ } -+ -+ /* Adjust Tx/Rx clock delay */ -+ clk_value &= ~(0x07 << 10); -+ clk_value |= ((tx_delay & 0x07) << 10); -+ clk_value &= ~(0x1F << 5); -+ clk_value |= ((rx_delay & 0x1F) << 5); -+ -+ writel(clk_value, priv->base_phy); -+} -+ -+static void geth_clk_disable(struct geth_priv *priv) -+{ -+ if (((priv->phy_ext == INT_PHY) || g_use_ephy_clk) && !IS_ERR_OR_NULL(priv->ephy_clk)) -+ clk_disable_unprepare(priv->ephy_clk); -+ -+ clk_disable_unprepare(priv->geth_clk); -+} -+ -+static void geth_tx_err(struct geth_priv *priv) -+{ -+ netif_stop_queue(priv->ndev); -+ -+ sunxi_stop_tx(priv->base); -+ -+ geth_free_tx_sk(priv); -+ memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc)); -+ desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx); -+ priv->tx_dirty = 0; -+ priv->tx_clean = 0; -+ sunxi_start_tx(priv->base, priv->dma_tx_phy); -+ -+ priv->ndev->stats.tx_errors++; -+ netif_wake_queue(priv->ndev); -+} -+ -+static inline void geth_schedule(struct geth_priv *priv) -+{ -+ if (likely(napi_schedule_prep(&priv->napi))) { -+ sunxi_int_disable(priv->base); -+ __napi_schedule(&priv->napi); -+ } -+} -+ -+static irqreturn_t geth_interrupt(int irq, void *dev_id) -+{ -+ struct net_device *ndev = (struct net_device *)dev_id; -+ struct geth_priv *priv = netdev_priv(ndev); -+ int status; -+ -+ if (unlikely(!ndev)) { -+ pr_err("%s: invalid ndev pointer\n", __func__); -+ return IRQ_NONE; -+ } -+ -+ status = sunxi_int_status(priv->base, (void *)(&priv->xstats)); -+ -+ if (likely(status == handle_tx_rx)) -+ geth_schedule(priv); -+ else if (unlikely(status == tx_hard_error_bump_tc)) -+ netdev_info(ndev, "Do nothing for bump tc\n"); -+ else if (unlikely(status == tx_hard_error)) -+ geth_tx_err(priv); -+ else -+ netdev_info(ndev, "Do nothing.....\n"); -+ -+ return IRQ_HANDLED; -+} -+ -+static int geth_open(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ int ret = 0; -+ -+ ret = geth_dma_desc_init(ndev); -+ if (ret) { -+ ret = -EINVAL; -+ return ret; -+ } -+ -+ ret = geth_power_on(priv); -+ if (ret) { -+ netdev_err(ndev, "Power on is failed\n"); -+ ret = -EINVAL; -+ goto power_err; -+ } -+ -+ geth_clk_enable(priv); -+ -+ ret = geth_phy_init(ndev); -+ if (ret) { -+ netdev_err(ndev, "phy init again...\n"); -+ ret = geth_phy_init(ndev); -+ if (ret) { -+ netdev_err(ndev, "phy init failed\n"); -+ ret = -EINVAL; -+ goto phy_err; -+ } -+ } -+ -+ ret = sunxi_mac_reset((void *)priv->base, &sunxi_udelay, 10000); -+ if (ret) { -+ netdev_err(ndev, "Initialize hardware error\n"); -+ goto mac_err; -+ } -+ -+ sunxi_mac_init(priv->base, txmode, rxmode); -+ sunxi_set_umac(priv->base, ndev->dev_addr, 0); -+ -+ memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc)); -+ memset(priv->dma_rx, 0, dma_desc_rx * sizeof(struct dma_desc)); -+ -+ desc_init_chain(priv->dma_rx, (unsigned long)priv->dma_rx_phy, dma_desc_rx); -+ desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx); -+ -+ priv->rx_clean = 0; -+ priv->rx_dirty = 0; -+ priv->tx_clean = 0; -+ priv->tx_dirty = 0; -+ geth_rx_refill(ndev); -+ -+ /* Extra statistics */ -+ memset(&priv->xstats, 0, sizeof(struct geth_extra_stats)); -+ -+ if (ndev->phydev) -+ phy_start(ndev->phydev); -+ -+ sunxi_start_rx(priv->base, (unsigned long)((struct dma_desc *)priv->dma_rx_phy + priv->rx_dirty)); -+ sunxi_start_tx(priv->base, (unsigned long)((struct dma_desc *)priv->dma_tx_phy + priv->tx_clean)); -+ -+ /* Enable the Rx/Tx */ -+ sunxi_mac_enable(priv->base); -+ -+ netif_carrier_on(ndev); -+ -+ napi_enable(&priv->napi); -+ netif_start_queue(ndev); -+ -+ return 0; -+ -+mac_err: -+ geth_phy_release(ndev); -+phy_err: -+ geth_clk_disable(priv); -+ -+ geth_power_off(priv); -+ -+power_err: -+ geth_free_dma_desc(priv); -+ -+ return ret; -+} -+ -+static int geth_stop(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ netif_stop_queue(ndev); -+ napi_disable(&priv->napi); -+ -+ netif_carrier_off(ndev); -+ -+ /* Release PHY resources */ -+ geth_phy_release(ndev); -+ -+ /* Disable Rx/Tx */ -+ sunxi_mac_disable(priv->base); -+ -+ geth_clk_disable(priv); -+ geth_power_off(priv); -+ -+ netif_tx_lock_bh(ndev); -+ /* Release the DMA TX/RX socket buffers */ -+ geth_free_rx_sk(priv); -+ geth_free_tx_sk(priv); -+ netif_tx_unlock_bh(ndev); -+ -+ /* Ensure that hareware have been stopped */ -+ geth_free_dma_desc(priv); -+ -+ return 0; -+} -+ -+static void geth_tx_complete(struct geth_priv *priv) -+{ -+ unsigned int entry = 0; -+ struct sk_buff *skb = NULL; -+ struct dma_desc *desc = NULL; -+ int tx_stat; -+ -+ spin_lock(&priv->tx_lock); -+ while (circ_cnt(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > 0) { -+ entry = priv->tx_clean; -+ desc = priv->dma_tx + entry; -+ -+ /* Check if the descriptor is owned by the DMA. */ -+ if (desc_get_own(desc)) -+ break; -+ -+ /* Verify tx error by looking at the last segment */ -+ if (desc_get_tx_ls(desc)) { -+ tx_stat = desc_get_tx_status(desc, (void *)(&priv->xstats)); -+ -+ if (likely(!tx_stat)) -+ priv->ndev->stats.tx_packets++; -+ else -+ priv->ndev->stats.tx_errors++; -+ } -+ -+ dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc), desc_buf_get_len(desc), DMA_TO_DEVICE); -+ -+ skb = priv->tx_sk[entry]; -+ priv->tx_sk[entry] = NULL; -+ desc_init(desc); -+ -+ /* Find next dirty desc */ -+ priv->tx_clean = circ_inc(entry, dma_desc_tx); -+ -+ if (unlikely(skb == NULL)) -+ continue; -+ -+ dev_kfree_skb(skb); -+ } -+ -+ if (unlikely(netif_queue_stopped(priv->ndev)) && circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > TX_THRESH) { -+ netif_wake_queue(priv->ndev); -+ } -+ spin_unlock(&priv->tx_lock); -+} -+ -+static netdev_tx_t geth_xmit(struct sk_buff *skb, struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ unsigned int entry; -+ struct dma_desc *desc, *first; -+ unsigned int len, tmp_len = 0; -+ int i, csum_insert; -+ int nfrags = skb_shinfo(skb)->nr_frags; -+ dma_addr_t paddr; -+ -+ spin_lock(&priv->tx_lock); -+ if (unlikely(circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) < (nfrags + 1))) { -+ if (!netif_queue_stopped(ndev)) { -+ netdev_err(ndev, "%s: BUG! Tx Ring full when queue awake\n", __func__); -+ netif_stop_queue(ndev); -+ } -+ spin_unlock(&priv->tx_lock); -+ -+ return NETDEV_TX_BUSY; -+ } -+ -+ csum_insert = (skb->ip_summed == CHECKSUM_PARTIAL); -+ entry = priv->tx_dirty; -+ first = priv->dma_tx + entry; -+ desc = priv->dma_tx + entry; -+ -+ len = skb_headlen(skb); -+ priv->tx_sk[entry] = skb; -+ -+#ifdef PKT_DEBUG -+ printk("======TX PKT DATA: ============\n"); -+ /* dump the packet */ -+ print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE, -+ 16, 1, skb->data, 64, true); -+#endif -+ -+ /* Every desc max size is 2K */ -+ while (len != 0) { -+ desc = priv->dma_tx + entry; -+ tmp_len = ((len > MAX_BUF_SZ) ? MAX_BUF_SZ : len); -+ -+ paddr = dma_map_single(priv->dev, skb->data, tmp_len, DMA_TO_DEVICE); -+ if (dma_mapping_error(priv->dev, paddr)) { -+ dev_kfree_skb(skb); -+ return -EIO; -+ } -+ desc_buf_set(desc, paddr, tmp_len); -+ /* Don't set the first's own bit, here */ -+ if (first != desc) { -+ priv->tx_sk[entry] = NULL; -+ desc_set_own(desc); -+ } -+ -+ entry = circ_inc(entry, dma_desc_tx); -+ len -= tmp_len; -+ } -+ -+ for (i = 0; i < nfrags; i++) { -+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ len = skb_frag_size(frag); -+ desc = priv->dma_tx + entry; -+ paddr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE); -+ if (dma_mapping_error(priv->dev, paddr)) { -+ dev_kfree_skb(skb); -+ return -EIO; -+ } -+ -+ desc_buf_set(desc, paddr, len); -+ desc_set_own(desc); -+ priv->tx_sk[entry] = NULL; -+ entry = circ_inc(entry, dma_desc_tx); -+ } -+ -+ ndev->stats.tx_bytes += skb->len; -+ priv->tx_dirty = entry; -+ desc_tx_close(first, desc, csum_insert); -+ -+ desc_set_own(first); -+ spin_unlock(&priv->tx_lock); -+ -+ if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) <= (MAX_SKB_FRAGS + 1)) { -+ netif_stop_queue(ndev); -+ if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > TX_THRESH) -+ netif_wake_queue(ndev); -+ } -+ -+#ifdef DEBUG -+ printk("=======TX Descriptor DMA: 0x%08llx\n", priv->dma_tx_phy); -+ printk("Tx pointor: dirty: %d, clean: %d\n", priv->tx_dirty, priv->tx_clean); -+ desc_print(priv->dma_tx, dma_desc_tx); -+#endif -+ sunxi_tx_poll(priv->base); -+ geth_tx_complete(priv); -+ -+ return NETDEV_TX_OK; -+} -+ -+static int geth_rx(struct geth_priv *priv, int limit) -+{ -+ unsigned int rxcount = 0; -+ unsigned int entry; -+ struct dma_desc *desc; -+ struct sk_buff *skb; -+ int status; -+ int frame_len; -+ -+ while (rxcount < limit) { -+ entry = priv->rx_dirty; -+ desc = priv->dma_rx + entry; -+ -+ if (desc_get_own(desc)) -+ break; -+ -+ rxcount++; -+ priv->rx_dirty = circ_inc(priv->rx_dirty, dma_desc_rx); -+ -+ /* Get length & status from hardware */ -+ frame_len = desc_rx_frame_len(desc); -+ status = desc_get_rx_status(desc, (void *)(&priv->xstats)); -+ -+ netdev_dbg(priv->ndev, "Rx frame size %d, status: %d\n", frame_len, status); -+ -+ skb = priv->rx_sk[entry]; -+ if (unlikely(!skb)) { -+ netdev_err(priv->ndev, "Skb is null\n"); -+ priv->ndev->stats.rx_dropped++; -+ break; -+ } -+ -+#ifdef PKT_DEBUG -+ printk("======RX PKT DATA: ============\n"); -+ /* dump the packet */ -+ print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE, -+ 16, 1, skb->data, 64, true); -+#endif -+ -+ if (status == discard_frame) { -+ netdev_dbg(priv->ndev, "Get error pkt\n"); -+ priv->ndev->stats.rx_errors++; -+ continue; -+ } -+ -+ if (unlikely(status != llc_snap)) -+ frame_len -= ETH_FCS_LEN; -+ -+ priv->rx_sk[entry] = NULL; -+ -+ skb_put(skb, frame_len); -+ dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc), desc_buf_get_len(desc), DMA_FROM_DEVICE); -+ -+ skb->protocol = eth_type_trans(skb, priv->ndev); -+ -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ napi_gro_receive(&priv->napi, skb); -+ -+ priv->ndev->stats.rx_packets++; -+ priv->ndev->stats.rx_bytes += frame_len; -+ } -+ -+#ifdef DEBUG -+ if (rxcount > 0) { -+ printk("======RX Descriptor DMA: 0x%08llx=\n", priv->dma_rx_phy); -+ printk("RX pointor: dirty: %d, clean: %d\n", priv->rx_dirty, priv->rx_clean); -+ desc_print(priv->dma_rx, dma_desc_rx); -+ } -+#endif -+ geth_rx_refill(priv->ndev); -+ -+ return rxcount; -+} -+ -+static int geth_poll(struct napi_struct *napi, int budget) -+{ -+ struct geth_priv *priv = container_of(napi, struct geth_priv, napi); -+ int work_done = 0; -+ -+ geth_tx_complete(priv); -+ work_done = geth_rx(priv, budget); -+ -+ if (work_done < budget) { -+ napi_complete(napi); -+ sunxi_int_enable(priv->base); -+ } -+ -+ return work_done; -+} -+ -+static int geth_change_mtu(struct net_device *ndev, int new_mtu) -+{ -+ int max_mtu; -+ -+ if (netif_running(ndev)) { -+ pr_err("%s: must be stopped to change its MTU\n", ndev->name); -+ return -EBUSY; -+ } -+ -+ max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); -+ -+ if ((new_mtu < 46) || (new_mtu > max_mtu)) { -+ pr_err("%s: invalid MTU, max MTU is: %d\n", ndev->name, max_mtu); -+ return -EINVAL; -+ } -+ -+ ndev->mtu = new_mtu; -+ netdev_update_features(ndev); -+ -+ return 0; -+} -+ -+static netdev_features_t geth_fix_features(struct net_device *ndev, netdev_features_t features) -+{ -+ return features; -+} -+ -+static void geth_set_rx_mode(struct net_device *ndev) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ unsigned int value = 0; -+ -+ pr_debug("%s: # mcasts %d, # unicast %d\n", -+ __func__, netdev_mc_count(ndev), netdev_uc_count(ndev)); -+ -+ spin_lock(&priv->lock); -+ if (ndev->flags & IFF_PROMISC) { -+ value = GETH_FRAME_FILTER_PR; -+ } else if ((netdev_mc_count(ndev) > HASH_TABLE_SIZE) || -+ (ndev->flags & IFF_ALLMULTI)) { -+ value = GETH_FRAME_FILTER_PM; /* pass all multi */ -+ sunxi_hash_filter(priv->base, ~0UL, ~0UL); -+ } else if (!netdev_mc_empty(ndev)) { -+ u32 mc_filter[2]; -+ struct netdev_hw_addr *ha; -+ -+ /* Hash filter for multicast */ -+ value = GETH_FRAME_FILTER_HMC; -+ -+ memset(mc_filter, 0, sizeof(mc_filter)); -+ netdev_for_each_mc_addr(ha, ndev) { -+ /* The upper 6 bits of the calculated CRC are used to -+ * index the contens of the hash table -+ */ -+ int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; -+ /* The most significant bit determines the register to -+ * use (H/L) while the other 5 bits determine the bit -+ * within the register. -+ */ -+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); -+ } -+ sunxi_hash_filter(priv->base, mc_filter[0], mc_filter[1]); -+ } -+ -+ /* Handle multiple unicast addresses (perfect filtering)*/ -+ if (netdev_uc_count(ndev) > 16) { -+ /* Switch to promiscuous mode is more than 8 addrs are required */ -+ value |= GETH_FRAME_FILTER_PR; -+ } else { -+ int reg = 1; -+ struct netdev_hw_addr *ha; -+ -+ netdev_for_each_uc_addr(ha, ndev) { -+ sunxi_set_umac(priv->base, ha->addr, reg); -+ reg++; -+ } -+ } -+ -+#ifdef FRAME_FILTER_DEBUG -+ /* Enable Receive all mode (to debug filtering_fail errors) */ -+ value |= GETH_FRAME_FILTER_RA; -+#endif -+ sunxi_set_filter(priv->base, value); -+ spin_unlock(&priv->lock); -+} -+ -+static void geth_tx_timeout(struct net_device *ndev, unsigned int txqueue) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ geth_tx_err(priv); -+} -+ -+static int geth_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -+{ -+ if (!netif_running(ndev)) -+ return -EINVAL; -+ -+ if (!ndev->phydev) -+ return -EINVAL; -+ -+ return phy_mii_ioctl(ndev->phydev, rq, cmd); -+} -+ -+/* Configuration changes (passed on by ifconfig) */ -+static int geth_config(struct net_device *ndev, struct ifmap *map) -+{ -+ if (ndev->flags & IFF_UP) /* can't act on a running interface */ -+ return -EBUSY; -+ -+ /* Don't allow changing the I/O address */ -+ if (map->base_addr != ndev->base_addr) { -+ pr_warn("%s: can't change I/O address\n", ndev->name); -+ return -EOPNOTSUPP; -+ } -+ -+ /* Don't allow changing the IRQ */ -+ if (map->irq != ndev->irq) { -+ pr_warn("%s: can't change IRQ number %d\n", ndev->name, ndev->irq); -+ return -EOPNOTSUPP; -+ } -+ -+ return 0; -+} -+ -+static int geth_set_mac_address(struct net_device *ndev, void *p) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct sockaddr *addr = p; -+ -+ if (!is_valid_ether_addr(addr->sa_data)) -+ return -EADDRNOTAVAIL; -+ -+ eth_hw_addr_set(ndev, addr->sa_data); -+ -+ sunxi_set_umac(priv->base, ndev->dev_addr, 0); -+ -+ return 0; -+} -+ -+int geth_set_features(struct net_device *ndev, netdev_features_t features) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ if (features & NETIF_F_LOOPBACK && netif_running(ndev)) -+ sunxi_mac_loopback(priv->base, 1); -+ else -+ sunxi_mac_loopback(priv->base, 0); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_NET_POLL_CONTROLLER -+/* Polling receive - used by NETCONSOLE and other diagnostic tools -+ * to allow network I/O with interrupts disabled. -+ */ -+static void geth_poll_controller(struct net_device *dev) -+{ -+ disable_irq(dev->irq); -+ geth_interrupt(dev->irq, dev); -+ enable_irq(dev->irq); -+} -+#endif -+ -+static const struct net_device_ops geth_netdev_ops = { -+ .ndo_init = NULL, -+ .ndo_open = geth_open, -+ .ndo_start_xmit = geth_xmit, -+ .ndo_stop = geth_stop, -+ .ndo_change_mtu = geth_change_mtu, -+ .ndo_fix_features = geth_fix_features, -+ .ndo_set_rx_mode = geth_set_rx_mode, -+ .ndo_tx_timeout = geth_tx_timeout, -+ .ndo_do_ioctl = geth_ioctl, -+ .ndo_set_config = geth_config, -+#ifdef CONFIG_NET_POLL_CONTROLLER -+ .ndo_poll_controller = geth_poll_controller, -+#endif -+ .ndo_set_mac_address = geth_set_mac_address, -+ .ndo_set_features = geth_set_features, -+}; -+ -+static int geth_check_if_running(struct net_device *ndev) -+{ -+ if (!netif_running(ndev)) -+ return -EBUSY; -+ return 0; -+} -+ -+static int geth_get_sset_count(struct net_device *netdev, int sset) -+{ -+ int len; -+ -+ switch (sset) { -+ case ETH_SS_STATS: -+ len = 0; -+ return len; -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+#if 0 -+static int geth_ethtool_getsettings(struct net_device *ndev, -+ struct ethtool_cmd *cmd) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct phy_device *phy = ndev->phydev; -+ int rc; -+ -+ if (phy == NULL) { -+ netdev_err(ndev, "%s: %s: PHY is not registered\n", -+ __func__, ndev->name); -+ return -ENODEV; -+ } -+ -+ if (!netif_running(ndev)) { -+ pr_err("%s: interface is disabled: we cannot track " -+ "link speed / duplex setting\n", ndev->name); -+ return -EBUSY; -+ } -+ -+ cmd->transceiver = XCVR_INTERNAL; -+ spin_lock_irq(&priv->lock); -+ rc = phy_ethtool_gset(phy, cmd); -+ spin_unlock_irq(&priv->lock); -+ -+ return rc; -+} -+ -+static int geth_ethtool_setsettings(struct net_device *ndev, -+ struct ethtool_cmd *cmd) -+{ -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct phy_device *phy = ndev->phydev; -+ int rc; -+ -+ spin_lock(&priv->lock); -+ rc = phy_ethtool_sset(phy, cmd); -+ spin_unlock(&priv->lock); -+ -+ return rc; -+} -+#endif -+ -+static void geth_ethtool_getdrvinfo(struct net_device *ndev, -+ struct ethtool_drvinfo *info) -+{ -+ strscpy(info->driver, "sunxi_geth", sizeof(info->driver)); -+ -+#define DRV_MODULE_VERSION "SUNXI Gbgit driver V1.1" -+ -+ strcpy(info->version, DRV_MODULE_VERSION); -+ info->fw_version[0] = '\0'; -+} -+ -+static const struct ethtool_ops geth_ethtool_ops = { -+ .begin = geth_check_if_running, -+ // .get_settings = geth_ethtool_getsettings, -+ // .set_settings = geth_ethtool_setsettings, -+ .get_link_ksettings = phy_ethtool_get_link_ksettings, -+ .set_link_ksettings = phy_ethtool_set_link_ksettings, -+ .get_link = ethtool_op_get_link, -+ .get_pauseparam = NULL, -+ .set_pauseparam = NULL, -+ .get_ethtool_stats = NULL, -+ .get_strings = NULL, -+ .get_wol = NULL, -+ .set_wol = NULL, -+ .get_sset_count = geth_get_sset_count, -+ .get_drvinfo = geth_ethtool_getdrvinfo, -+}; -+ -+struct gpio_config { -+ u32 gpio; -+ u32 mul_sel; -+ u32 pull; -+ u32 drv_level; -+ u32 data; -+}; -+ -+/* config hardware resource */ -+static int geth_hw_init(struct platform_device *pdev) -+{ -+ struct net_device *ndev = platform_get_drvdata(pdev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ struct device_node *np = pdev->dev.of_node; -+ int ret = 0; -+ struct resource *res; -+ u32 value; -+ struct gpio_config cfg; -+ const char *gmac_power; -+ char power[20]; -+ int i; -+ -+// #ifdef CONFIG_SUNXI_EXT_PHY -+ priv->phy_ext = EXT_PHY; -+// #else -+// priv->phy_ext = INT_PHY; -+// #endif -+ -+ /* config memery resource */ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (unlikely(!res)) { -+ pr_err("%s: ERROR: get gmac memory failed", __func__); -+ return -ENODEV; -+ } -+ -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (!priv->base) { -+ pr_err("%s: ERROR: gmac memory mapping failed", __func__); -+ return -ENOMEM; -+ } -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); -+ if (unlikely(!res)) { -+ pr_err("%s: ERROR: get phy memory failed", __func__); -+ ret = -ENODEV; -+ goto mem_err; -+ } -+ -+ priv->base_phy = devm_ioremap_resource(&pdev->dev, res); -+ if (unlikely(!priv->base_phy)) { -+ pr_err("%s: ERROR: phy memory mapping failed", __func__); -+ ret = -ENOMEM; -+ goto mem_err; -+ } -+ -+ /* config IRQ */ -+ ndev->irq = platform_get_irq_byname(pdev, "gmacirq"); -+ if (ndev->irq == -ENXIO) { -+ pr_err("%s: ERROR: MAC IRQ not found\n", __func__); -+ ret = -ENXIO; -+ goto irq_err; -+ } -+ -+ ret = request_irq(ndev->irq, geth_interrupt, IRQF_SHARED, dev_name(&pdev->dev), ndev); -+ if (unlikely(ret < 0)) { -+ pr_err("Could not request irq %d, error: %d\n", ndev->irq, ret); -+ goto irq_err; -+ } -+ -+ /* config clock */ -+ priv->geth_clk = of_clk_get_by_name(np, "bus-emac1"); -+ if (unlikely(!priv->geth_clk || IS_ERR(priv->geth_clk))) { -+ pr_err("Get gmac clock failed!\n"); -+ ret = -EINVAL; -+ goto clk_err; -+ } -+ -+ if (INT_PHY == priv->phy_ext) { -+ priv->ephy_clk = of_clk_get_by_name(np, "emac-25m"); -+ if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) { -+ pr_err("Get ephy clock failed!\n"); -+ ret = -EINVAL; -+ goto clk_err; -+ } -+ } -+#if 1 // defined(CONFIG_ARCH_SUN8IW12) || defined(CONFIG_ARCH_SUN50IW9) -+ else { -+ if (!of_property_read_u32(np, "use_ephy25m", &g_use_ephy_clk) -+ && g_use_ephy_clk) { -+ priv->ephy_clk = of_clk_get_by_name(np, "ephy"); -+ if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) { -+ pr_err("Get ephy clk failed!\n"); -+ ret = -EINVAL; -+ goto clk_err; -+ } -+ } -+ } -+#endif -+ -+ /* config power regulator */ -+ if (EXT_PHY == priv->phy_ext) { -+ for (i = 0; i < POWER_CHAN_NUM; i++) { -+ snprintf(power, 15, "gmac-power%d", i); -+ ret = of_property_read_string(np, power, &gmac_power); -+ if (ret) { -+ priv->gmac_power[i] = NULL; -+ pr_info("gmac-power%d: NULL\n", i); -+ continue; -+ } -+ priv->gmac_power[i] = regulator_get(NULL, gmac_power); -+ if (IS_ERR(priv->gmac_power[i])) { -+ pr_err("gmac-power%d get error!\n", i); -+ ret = -EINVAL; -+ goto clk_err; -+ } -+ } -+ } -+ -+ // /* config other parameters */ -+ // priv->phy_interface = of_get_phy_mode(np); -+ // if (priv->phy_interface != PHY_INTERFACE_MODE_MII && -+ // priv->phy_interface != PHY_INTERFACE_MODE_RGMII && -+ // priv->phy_interface != PHY_INTERFACE_MODE_RMII) { -+ // pr_err("Not support phy type!\n"); -+ // priv->phy_interface = PHY_INTERFACE_MODE_MII; -+ // } -+ -+ priv->phy_interface = PHY_INTERFACE_MODE_RMII; -+ -+ if (!of_property_read_u32(np, "tx-delay", &value)) -+ tx_delay = value; -+ -+ if (!of_property_read_u32(np, "rx-delay", &value)) -+ rx_delay = value; -+ -+ /* config pinctrl */ -+ if (EXT_PHY == priv->phy_ext) { -+ priv->phyrst = of_get_named_gpio_flags(np, "phy-rst", 0, (enum of_gpio_flags *)&cfg); -+ priv->rst_active_low = (cfg.data == OF_GPIO_ACTIVE_LOW) ? 1 : 0; -+ -+ if (gpio_is_valid(priv->phyrst)) { -+ if (gpio_request(priv->phyrst, "phy-rst") < 0) { -+ pr_err("gmac gpio request fail!\n"); -+ ret = -EINVAL; -+ goto pin_err; -+ } -+ } -+ -+ priv->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); -+ if (IS_ERR_OR_NULL(priv->pinctrl)) { -+ pr_err("gmac pinctrl error!\n"); -+ priv->pinctrl = NULL; -+ ret = -EINVAL; -+ goto pin_err; -+ } -+ } -+ -+ priv->ephy_rst = devm_reset_control_get_optional(&pdev->dev, "stmmaceth"); -+ reset_control_assert(priv->ephy_rst); -+ reset_control_deassert(priv->ephy_rst); -+ -+ msleep(800); -+ -+ return 0; -+ -+pin_err: -+ if (EXT_PHY == priv->phy_ext) { -+ for (i = 0; i < POWER_CHAN_NUM; i++) { -+ if (IS_ERR_OR_NULL(priv->gmac_power[i])) -+ continue; -+ regulator_put(priv->gmac_power[i]); -+ } -+ } -+clk_err: -+ free_irq(ndev->irq, ndev); -+irq_err: -+ devm_iounmap(&pdev->dev, priv->base_phy); -+mem_err: -+ devm_iounmap(&pdev->dev, priv->base); -+ -+ return ret; -+} -+ -+static void geth_hw_release(struct platform_device *pdev) -+{ -+ struct net_device *ndev = platform_get_drvdata(pdev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ int i; -+ -+ devm_iounmap(&pdev->dev, (priv->base_phy)); -+ devm_iounmap(&pdev->dev, priv->base); -+ free_irq(ndev->irq, ndev); -+ if (priv->geth_clk) -+ clk_put(priv->geth_clk); -+ -+ if (EXT_PHY == priv->phy_ext) { -+ for (i = 0; i < POWER_CHAN_NUM; i++) { -+ if (IS_ERR_OR_NULL(priv->gmac_power[i])) -+ continue; -+ regulator_put(priv->gmac_power[i]); -+ } -+ -+ if (!IS_ERR_OR_NULL(priv->pinctrl)) -+ devm_pinctrl_put(priv->pinctrl); -+ -+ if (gpio_is_valid(priv->phyrst)) -+ gpio_free(priv->phyrst); -+ } -+ -+ if (!IS_ERR_OR_NULL(priv->ephy_clk)) -+ clk_put(priv->ephy_clk); -+} -+ -+/** -+ * geth_probe -+ * @pdev: platform device pointer -+ * Description: the driver is initialized through platform_device. -+ */ -+static int geth_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ struct net_device *ndev = NULL; -+ struct geth_priv *priv; -+ -+#ifdef CONFIG_OF -+ pdev->dev.dma_mask = &geth_dma_mask; -+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); -+#endif -+ -+ ndev = alloc_etherdev(sizeof(struct geth_priv)); -+ if (!ndev) { -+ dev_err(&pdev->dev, "could not allocate device.\n"); -+ return -ENOMEM; -+ } -+ SET_NETDEV_DEV(ndev, &pdev->dev); -+ -+ priv = netdev_priv(ndev); -+ platform_set_drvdata(pdev, ndev); -+ -+ /* Must set private data to pdev, before call it */ -+ ret = geth_hw_init(pdev); -+ if (0 != ret) { -+ pr_err("geth_hw_init fail!\n"); -+ goto hw_err; -+ } -+ -+ /* setup the netdevice, fill the field of netdevice */ -+ ether_setup(ndev); -+ ndev->netdev_ops = &geth_netdev_ops; -+ netdev_set_default_ethtool_ops(ndev, &geth_ethtool_ops); -+ ndev->base_addr = (unsigned long)priv->base; -+ -+ priv->ndev = ndev; -+ priv->dev = &pdev->dev; -+ -+ /* TODO: support the VLAN frames */ -+ ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | -+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; -+ -+ ndev->features |= ndev->hw_features; -+ ndev->hw_features |= NETIF_F_LOOPBACK; -+ ndev->priv_flags |= IFF_UNICAST_FLT; -+ -+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog); -+ -+ netif_napi_add(ndev, &priv->napi, geth_poll); -+ -+ spin_lock_init(&priv->lock); -+ spin_lock_init(&priv->tx_lock); -+ -+ /* The last val is mdc clock ratio */ -+ sunxi_geth_register((void *)ndev->base_addr, HW_VERSION, 0x03); -+ -+ ret = register_netdev(ndev); -+ if (ret) { -+ netif_napi_del(&priv->napi); -+ pr_err("Error: Register %s failed\n", ndev->name); -+ goto reg_err; -+ } -+ -+ /* Before open the device, the mac address should be set */ -+ geth_check_addr(ndev, mac_str); -+ -+#ifdef CONFIG_GETH_ATTRS -+ geth_create_attrs(ndev); -+#endif -+ device_create_file(&pdev->dev, &dev_attr_gphy_test); -+ device_create_file(&pdev->dev, &dev_attr_mii_read); -+ device_create_file(&pdev->dev, &dev_attr_mii_write); -+ -+ device_enable_async_suspend(&pdev->dev); -+ -+#ifdef CONFIG_PM -+ INIT_WORK(&priv->eth_work, geth_resume_work); -+#endif -+ -+ return 0; -+ -+reg_err: -+ geth_hw_release(pdev); -+hw_err: -+ platform_set_drvdata(pdev, NULL); -+ free_netdev(ndev); -+ -+ return ret; -+} -+ -+static int geth_remove(struct platform_device *pdev) -+{ -+ struct net_device *ndev = platform_get_drvdata(pdev); -+ struct geth_priv *priv = netdev_priv(ndev); -+ -+ device_remove_file(&pdev->dev, &dev_attr_gphy_test); -+ device_remove_file(&pdev->dev, &dev_attr_mii_read); -+ device_remove_file(&pdev->dev, &dev_attr_mii_write); -+ -+ netif_napi_del(&priv->napi); -+ unregister_netdev(ndev); -+ geth_hw_release(pdev); -+ platform_set_drvdata(pdev, NULL); -+ free_netdev(ndev); -+ -+ return 0; -+} -+ -+static const struct of_device_id geth_of_match[] = { -+ {.compatible = "allwinner,sunxi-gmac",}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, geth_of_match); -+ -+static struct platform_driver geth_driver = { -+ .probe = geth_probe, -+ .remove = geth_remove, -+ .suspend = ucc_geth_suspend, -+ .resume = ucc_geth_resume, -+ .driver = { -+ .name = "sunxi-gmac", -+ .owner = THIS_MODULE, -+ .pm = &geth_pm_ops, -+ .of_match_table = geth_of_match, -+ }, -+}; -+module_platform_driver(geth_driver); -+ -+#ifndef MODULE -+static int __init set_mac_addr(char *str) -+{ -+ char *p = str; -+ -+ if (str && strlen(str)) -+ memcpy(mac_str, p, 18); -+ -+ return 0; -+} -+__setup("mac_addr=", set_mac_addr); -+#endif -+ -+MODULE_DESCRIPTION("Allwinner Gigabit Ethernet driver"); -+MODULE_AUTHOR("fuzhaoke "); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/ethernet/allwinner/sunxi-gmac.h b/drivers/net/ethernet/allwinner/sunxi-gmac.h -new file mode 100644 -index 000000000000..0ba8977d28f4 ---- /dev/null -+++ b/drivers/net/ethernet/allwinner/sunxi-gmac.h -@@ -0,0 +1,270 @@ -+/* -+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h -+ * -+ * Copyright © 2016-2018, fuzhaoke -+ * Author: fuzhaoke -+ * -+ * This file is provided under a dual BSD/GPL license. When using or -+ * redistributing this file, you may do so under either 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. -+ */ -+#ifndef __SUNXI_GETH_H__ -+#define __SUNXI_GETH_H__ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* GETH_FRAME_FILTER register value */ -+#define GETH_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */ -+#define GETH_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */ -+#define GETH_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */ -+#define GETH_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */ -+#define GETH_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */ -+#define GETH_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */ -+#define GETH_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ -+#define GETH_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ -+#define GETH_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ -+#define GETH_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ -+ -+/* Default tx descriptor */ -+#define TX_SINGLE_DESC0 0x80000000 -+#define TX_SINGLE_DESC1 0x63000000 -+ -+/* Default rx descriptor */ -+#define RX_SINGLE_DESC0 0x80000000 -+#define RX_SINGLE_DESC1 0x83000000 -+ -+#define EPHY_ID 0x00441400 -+#define AC300_ID 0xc0000000 -+#define EPHY_ID_RTL8211F 0x001CC916 -+#define IP101G_ID 0x02430c54 -+ -+typedef union { -+ struct { -+ /* TDES0 */ -+ unsigned int deferred:1; /* Deferred bit (only half-duplex) */ -+ unsigned int under_err:1; /* Underflow error */ -+ unsigned int ex_deferral:1; /* Excessive deferral */ -+ unsigned int coll_cnt:4; /* Collision count */ -+ unsigned int vlan_tag:1; /* VLAN Frame */ -+ unsigned int ex_coll:1; /* Excessive collision */ -+ unsigned int late_coll:1; /* Late collision */ -+ unsigned int no_carr:1; /* No carrier */ -+ unsigned int loss_carr:1; /* Loss of collision */ -+ unsigned int ipdat_err:1; /* IP payload error */ -+ unsigned int frm_flu:1; /* Frame flushed */ -+ unsigned int jab_timeout:1; /* Jabber timeout */ -+ unsigned int err_sum:1; /* Error summary */ -+ unsigned int iphead_err:1; /* IP header error */ -+ unsigned int ttss:1; /* Transmit time stamp status */ -+ unsigned int reserved0:13; -+ unsigned int own:1; /* Own bit. CPU:0, DMA:1 */ -+ } tx; -+ -+ /* bits 5 7 0 | Frame status -+ * ---------------------------------------------------------- -+ * 0 0 0 | IEEE 802.3 Type frame (length < 1536 octects) -+ * 1 0 0 | IPv4/6 No CSUM errorS. -+ * 1 0 1 | IPv4/6 CSUM PAYLOAD error -+ * 1 1 0 | IPv4/6 CSUM IP HR error -+ * 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS -+ * 0 0 1 | IPv4/6 unsupported IP PAYLOAD -+ * 0 1 1 | COE bypassed.. no IPv4/6 frame -+ * 0 1 0 | Reserved. -+ */ -+ struct { -+ /* RDES0 */ -+ unsigned int chsum_err:1; /* Payload checksum error */ -+ unsigned int crc_err:1; /* CRC error */ -+ unsigned int dribbling:1; /* Dribble bit error */ -+ unsigned int mii_err:1; /* Received error (bit3) */ -+ unsigned int recv_wt:1; /* Received watchdog timeout */ -+ unsigned int frm_type:1; /* Frame type */ -+ unsigned int late_coll:1; /* Late Collision */ -+ unsigned int ipch_err:1; /* IPv header checksum error (bit7) */ -+ unsigned int last_desc:1; /* Laset descriptor */ -+ unsigned int first_desc:1; /* First descriptor */ -+ unsigned int vlan_tag:1; /* VLAN Tag */ -+ unsigned int over_err:1; /* Overflow error (bit11) */ -+ unsigned int len_err:1; /* Length error */ -+ unsigned int sou_filter:1; /* Source address filter fail */ -+ unsigned int desc_err:1; /* Descriptor error */ -+ unsigned int err_sum:1; /* Error summary (bit15) */ -+ unsigned int frm_len:14; /* Frame length */ -+ unsigned int des_filter:1; /* Destination address filter fail */ -+ unsigned int own:1; /* Own bit. CPU:0, DMA:1 */ -+ #define RX_PKT_OK 0x7FFFB77C -+ #define RX_LEN 0x3FFF0000 -+ } rx; -+ -+ unsigned int all; -+} desc0_u; -+ -+typedef union { -+ struct { -+ /* TDES1 */ -+ unsigned int buf1_size:11; /* Transmit buffer1 size */ -+ unsigned int buf2_size:11; /* Transmit buffer2 size */ -+ unsigned int ttse:1; /* Transmit time stamp enable */ -+ unsigned int dis_pad:1; /* Disable pad (bit23) */ -+ unsigned int adr_chain:1; /* Second address chained */ -+ unsigned int end_ring:1; /* Transmit end of ring */ -+ unsigned int crc_dis:1; /* Disable CRC */ -+ unsigned int cic:2; /* Checksum insertion control (bit27:28) */ -+ unsigned int first_sg:1; /* First Segment */ -+ unsigned int last_seg:1; /* Last Segment */ -+ unsigned int interrupt:1; /* Interrupt on completion */ -+ } tx; -+ -+ struct { -+ /* RDES1 */ -+ unsigned int buf1_size:11; /* Received buffer1 size */ -+ unsigned int buf2_size:11; /* Received buffer2 size */ -+ unsigned int reserved1:2; -+ unsigned int adr_chain:1; /* Second address chained */ -+ unsigned int end_ring:1; /* Received end of ring */ -+ unsigned int reserved2:5; -+ unsigned int dis_ic:1; /* Disable interrupt on completion */ -+ } rx; -+ -+ unsigned int all; -+} desc1_u; -+ -+typedef struct dma_desc { -+ desc0_u desc0; -+ desc1_u desc1; -+ /* The address of buffers */ -+ unsigned int desc2; -+ /* Next desc's address */ -+ unsigned int desc3; -+} __attribute__((packed)) dma_desc_t; -+ -+enum rx_frame_status { /* IPC status */ -+ good_frame = 0, -+ discard_frame = 1, -+ csum_none = 2, -+ llc_snap = 4, -+}; -+ -+enum tx_dma_irq_status { -+ tx_hard_error = 1, -+ tx_hard_error_bump_tc = 2, -+ handle_tx_rx = 3, -+}; -+ -+struct geth_extra_stats { -+ /* Transmit errors */ -+ unsigned long tx_underflow; -+ unsigned long tx_carrier; -+ unsigned long tx_losscarrier; -+ unsigned long vlan_tag; -+ unsigned long tx_deferred; -+ unsigned long tx_vlan; -+ unsigned long tx_jabber; -+ unsigned long tx_frame_flushed; -+ unsigned long tx_payload_error; -+ unsigned long tx_ip_header_error; -+ -+ /* Receive errors */ -+ unsigned long rx_desc; -+ unsigned long sa_filter_fail; -+ unsigned long overflow_error; -+ unsigned long ipc_csum_error; -+ unsigned long rx_collision; -+ unsigned long rx_crc; -+ unsigned long dribbling_bit; -+ unsigned long rx_length; -+ unsigned long rx_mii; -+ unsigned long rx_multicast; -+ unsigned long rx_gmac_overflow; -+ unsigned long rx_watchdog; -+ unsigned long da_rx_filter_fail; -+ unsigned long sa_rx_filter_fail; -+ unsigned long rx_missed_cntr; -+ unsigned long rx_overflow_cntr; -+ unsigned long rx_vlan; -+ -+ /* Tx/Rx IRQ errors */ -+ unsigned long tx_undeflow_irq; -+ unsigned long tx_process_stopped_irq; -+ unsigned long tx_jabber_irq; -+ unsigned long rx_overflow_irq; -+ unsigned long rx_buf_unav_irq; -+ unsigned long rx_process_stopped_irq; -+ unsigned long rx_watchdog_irq; -+ unsigned long tx_early_irq; -+ unsigned long fatal_bus_error_irq; -+ -+ /* Extra info */ -+ unsigned long threshold; -+ unsigned long tx_pkt_n; -+ unsigned long rx_pkt_n; -+ unsigned long poll_n; -+ unsigned long sched_timer_n; -+ unsigned long normal_irq_n; -+}; -+ -+struct mii_reg_dump { -+ u32 addr; -+ u16 reg; -+ u16 value; -+}; -+ -+int sunxi_mdio_read(void *, int, int); -+int sunxi_mdio_write(void *, int, int, unsigned short); -+int sunxi_mdio_reset(void *); -+void sunxi_set_link_mode(void *iobase, int duplex, int speed); -+void sunxi_int_disable(void *); -+int sunxi_int_status(void *, struct geth_extra_stats *x); -+int sunxi_mac_init(void *, int txmode, int rxmode); -+void sunxi_set_umac(void *, unsigned char *, int); -+void sunxi_mac_enable(void *); -+void sunxi_mac_disable(void *); -+void sunxi_tx_poll(void *); -+void sunxi_int_enable(void *); -+void sunxi_start_rx(void *, unsigned long); -+void sunxi_start_tx(void *, unsigned long); -+void sunxi_stop_tx(void *); -+void sunxi_stop_rx(void *); -+void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high); -+void sunxi_set_filter(void *iobase, unsigned long flags); -+void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause); -+void sunxi_mac_loopback(void *iobase, int enable); -+ -+void desc_buf_set(struct dma_desc *p, unsigned long paddr, int size); -+void desc_set_own(struct dma_desc *p); -+void desc_init_chain(struct dma_desc *p, unsigned long paddr, unsigned int size); -+void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert); -+void desc_init(struct dma_desc *p); -+int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x); -+int desc_buf_get_len(struct dma_desc *desc); -+int desc_buf_get_addr(struct dma_desc *desc); -+int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x); -+int desc_get_own(struct dma_desc *desc); -+int desc_get_tx_ls(struct dma_desc *desc); -+int desc_rx_frame_len(struct dma_desc *desc); -+ -+int sunxi_mac_reset(void *iobase, void (*mdelay)(int), int n); -+int sunxi_geth_register(void *iobase, int version, unsigned int div); -+ -+int sunxi_parse_read_str(char *str, u16 *addr, u16 *reg); -+int sunxi_parse_write_str(char *str, u16 *addr, u16 *reg, u16 *val); -+extern int ephy_is_enable(void); -+extern int gmac_ephy_shutdown(void); -+ -+#if defined(CONFIG_ARCH_SUN8IW3) \ -+ || defined(CONFIG_ARCH_SUN9IW1) \ -+ || defined(CONFIG_ARCH_SUN7I) -+#define HW_VERSION 0 -+#else -+#define HW_VERSION 1 -+#endif -+ -+#endif -diff --git a/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c b/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c -new file mode 100644 -index 000000000000..926516835023 ---- /dev/null -+++ b/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c -@@ -0,0 +1,768 @@ -+/* -+ * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c -+ * -+ * Copyright © 2016-2018, fuzhaoke -+ * Author: fuzhaoke -+ * -+ * This file is provided under a dual BSD/GPL license. When using or -+ * redistributing this file, you may do so under either 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 -+#include -+#include -+#include -+#include "sunxi-gmac.h" -+ -+/****************************************************************************** -+ * sun8iw6 operations -+ *****************************************************************************/ -+#define GETH_BASIC_CTL0 0x00 -+#define GETH_BASIC_CTL1 0x04 -+#define GETH_INT_STA 0x08 -+#define GETH_INT_EN 0x0C -+#define GETH_TX_CTL0 0x10 -+#define GETH_TX_CTL1 0x14 -+#define GETH_TX_FLOW_CTL 0x1C -+#define GETH_TX_DESC_LIST 0x20 -+#define GETH_RX_CTL0 0x24 -+#define GETH_RX_CTL1 0x28 -+#define GETH_RX_DESC_LIST 0x34 -+#define GETH_RX_FRM_FLT 0x38 -+#define GETH_RX_HASH0 0x40 -+#define GETH_RX_HASH1 0x44 -+#define GETH_MDIO_ADDR 0x48 -+#define GETH_MDIO_DATA 0x4C -+#define GETH_ADDR_HI(reg) (0x50 + ((reg) << 3)) -+#define GETH_ADDR_LO(reg) (0x54 + ((reg) << 3)) -+#define GETH_TX_DMA_STA 0xB0 -+#define GETH_TX_CUR_DESC 0xB4 -+#define GETH_TX_CUR_BUF 0xB8 -+#define GETH_RX_DMA_STA 0xC0 -+#define GETH_RX_CUR_DESC 0xC4 -+#define GETH_RX_CUR_BUF 0xC8 -+#define GETH_RGMII_STA 0xD0 -+ -+#define RGMII_IRQ 0x00000001 -+ -+#define CTL0_LM 0x02 -+#define CTL0_DM 0x01 -+#define CTL0_SPEED 0x04 -+ -+#define BURST_LEN 0x3F000000 -+#define RX_TX_PRI 0x02 -+#define SOFT_RST 0x01 -+ -+#define TX_FLUSH 0x01 -+#define TX_MD 0x02 -+#define TX_NEXT_FRM 0x04 -+#define TX_TH 0x0700 -+ -+#define RX_FLUSH 0x01 -+#define RX_MD 0x02 -+#define RX_RUNT_FRM 0x04 -+#define RX_TH 0x0030 -+ -+#define TX_INT 0x00001 -+#define TX_STOP_INT 0x00002 -+#define TX_UA_INT 0x00004 -+#define TX_TOUT_INT 0x00008 -+#define TX_UNF_INT 0x00010 -+#define TX_EARLY_INT 0x00020 -+#define RX_INT 0x00100 -+#define RX_UA_INT 0x00200 -+#define RX_STOP_INT 0x00400 -+#define RX_TOUT_INT 0x00800 -+#define RX_OVF_INT 0x01000 -+#define RX_EARLY_INT 0x02000 -+#define LINK_STA_INT 0x10000 -+ -+#define DISCARD_FRAME -1 -+#define GOOD_FRAME 0 -+#define CSUM_NONE 2 -+#define LLC_SNAP 4 -+ -+#define SF_DMA_MODE 1 -+ -+/* Flow Control defines */ -+#define FLOW_OFF 0 -+#define FLOW_RX 1 -+#define FLOW_TX 2 -+#define FLOW_AUTO (FLOW_TX | FLOW_RX) -+ -+#define HASH_TABLE_SIZE 64 -+#define PAUSE_TIME 0x200 -+#define GMAC_MAX_UNICAST_ADDRESSES 8 -+ -+/* PHY address */ -+#define PHY_ADDR 0x01 -+#define PHY_DM 0x0010 -+#define PHY_AUTO_NEG 0x0020 -+#define PHY_POWERDOWN 0x0080 -+#define PHY_NEG_EN 0x1000 -+ -+#define MII_BUSY 0x00000001 -+#define MII_WRITE 0x00000002 -+#define MII_PHY_MASK 0x0000FFC0 -+#define MII_CR_MASK 0x0000001C -+#define MII_CLK 0x00000008 -+/* bits 4 3 2 | AHB1 Clock | MDC Clock -+ * ------------------------------------------------------- -+ * 0 0 0 | 60 ~ 100 MHz | div-42 -+ * 0 0 1 | 100 ~ 150 MHz | div-62 -+ * 0 1 0 | 20 ~ 35 MHz | div-16 -+ * 0 1 1 | 35 ~ 60 MHz | div-26 -+ * 1 0 0 | 150 ~ 250 MHz | div-102 -+ * 1 0 1 | 250 ~ 300 MHz | div-124 -+ * 1 1 x | Reserved | -+ */ -+ -+enum csum_insertion { -+ cic_dis = 0, /* Checksum Insertion Control */ -+ cic_ip = 1, /* Only IP header */ -+ cic_no_pse = 2, /* IP header but not pseudoheader */ -+ cic_full = 3, /* IP header and pseudoheader */ -+}; -+ -+struct gethdev { -+ void *iobase; -+ unsigned int ver; -+ unsigned int mdc_div; -+}; -+ -+static struct gethdev hwdev; -+ -+/*************************************************************************** -+ * External interface -+ **************************************************************************/ -+/* Set a ring desc buffer */ -+void desc_init_chain(struct dma_desc *desc, unsigned long addr, unsigned int size) -+{ -+ /* In chained mode the desc3 points to the next element in the ring. -+ * The latest element has to point to the head. -+ */ -+ int i; -+ struct dma_desc *p = desc; -+ unsigned long dma_phy = addr; -+ -+ for (i = 0; i < (size - 1); i++) { -+ dma_phy += sizeof(struct dma_desc); -+ p->desc3 = (unsigned int)dma_phy; -+ /* Chain mode */ -+ p->desc1.all |= (1 << 24); -+ p++; -+ } -+ p->desc1.all |= (1 << 24); -+ p->desc3 = (unsigned int)addr; -+} -+ -+int sunxi_mdio_read(void *iobase, int phyaddr, int phyreg) -+{ -+ unsigned int value = 0; -+ -+ /* Mask the MDC_DIV_RATIO */ -+ value |= ((hwdev.mdc_div & 0x07) << 20); -+ value |= (((phyaddr << 12) & (0x0001F000)) | -+ ((phyreg << 4) & (0x000007F0)) | -+ MII_BUSY); -+ -+ while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1) -+ ; -+ -+ writel(value, iobase + GETH_MDIO_ADDR); -+ while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1) -+ ; -+ -+ return (int)readl(iobase + GETH_MDIO_DATA); -+} -+ -+int sunxi_mdio_write(void *iobase, int phyaddr, int phyreg, unsigned short data) -+{ -+ unsigned int value; -+ -+ value = ((0x07 << 20) & readl(iobase + GETH_MDIO_ADDR)) | -+ (hwdev.mdc_div << 20); -+ value |= (((phyaddr << 12) & (0x0001F000)) | -+ ((phyreg << 4) & (0x000007F0))) | -+ MII_WRITE | MII_BUSY; -+ -+ /* Wait until any existing MII operation is complete */ -+ while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1) -+ ; -+ -+ /* Set the MII address register to write */ -+ writel(data, iobase + GETH_MDIO_DATA); -+ writel(value, iobase + GETH_MDIO_ADDR); -+ -+ /* Wait until any existing MII operation is complete */ -+ while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1) -+ ; -+ -+ return 0; -+} -+ -+int sunxi_mdio_reset(void *iobase) -+{ -+ writel((4 << 2), iobase + GETH_MDIO_ADDR); -+ return 0; -+} -+ -+void sunxi_set_link_mode(void *iobase, int duplex, int speed) -+{ -+ unsigned int ctrl = readl(iobase + GETH_BASIC_CTL0); -+ -+ if (!duplex) -+ ctrl &= ~CTL0_DM; -+ else -+ ctrl |= CTL0_DM; -+ -+ switch (speed) { -+ case 1000: -+ ctrl &= ~0x0C; -+ break; -+ case 100: -+ case 10: -+ default: -+ ctrl |= 0x08; -+ if (speed == 100) -+ ctrl |= 0x04; -+ else -+ ctrl &= ~0x04; -+ break; -+ } -+ -+ writel(ctrl, iobase + GETH_BASIC_CTL0); -+} -+ -+void sunxi_mac_loopback(void *iobase, int enable) -+{ -+ int reg; -+ -+ reg = readl(iobase + GETH_BASIC_CTL0); -+ if (enable) -+ reg |= 0x02; -+ else -+ reg &= ~0x02; -+ writel(reg, iobase + GETH_BASIC_CTL0); -+} -+ -+void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause) -+{ -+ unsigned int flow = 0; -+ -+ if (fc & FLOW_RX) { -+ flow = readl(iobase + GETH_RX_CTL0); -+ flow |= 0x10000; -+ writel(flow, iobase + GETH_RX_CTL0); -+ } -+ -+ if (fc & FLOW_TX) { -+ flow = readl(iobase + GETH_TX_FLOW_CTL); -+ flow |= 0x00001; -+ writel(flow, iobase + GETH_TX_FLOW_CTL); -+ } -+ -+ if (duplex) { -+ flow = readl(iobase + GETH_TX_FLOW_CTL); -+ flow |= (pause << 4); -+ writel(flow, iobase + GETH_TX_FLOW_CTL); -+ } -+} -+ -+int sunxi_int_status(void *iobase, struct geth_extra_stats *x) -+{ -+ int ret = 0; -+ /* read the status register (CSR5) */ -+ unsigned int intr_status; -+ -+ intr_status = readl(iobase + GETH_RGMII_STA); -+ if (intr_status & RGMII_IRQ) -+ readl(iobase + GETH_RGMII_STA); -+ -+ intr_status = readl(iobase + GETH_INT_STA); -+ -+ /* ABNORMAL interrupts */ -+ if (intr_status & TX_UNF_INT) { -+ ret = tx_hard_error_bump_tc; -+ x->tx_undeflow_irq++; -+ } -+ if (intr_status & TX_TOUT_INT) { -+ x->tx_jabber_irq++; -+ } -+ if (intr_status & RX_OVF_INT) { -+ x->rx_overflow_irq++; -+ } -+ if (intr_status & RX_UA_INT) { -+ x->rx_buf_unav_irq++; -+ } -+ if (intr_status & RX_STOP_INT) { -+ x->rx_process_stopped_irq++; -+ } -+ if (intr_status & RX_TOUT_INT) { -+ x->rx_watchdog_irq++; -+ } -+ if (intr_status & TX_EARLY_INT) { -+ x->tx_early_irq++; -+ } -+ if (intr_status & TX_STOP_INT) { -+ x->tx_process_stopped_irq++; -+ ret = tx_hard_error; -+ } -+ -+ /* TX/RX NORMAL interrupts */ -+ if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) { -+ x->normal_irq_n++; -+ if (intr_status & (TX_INT | RX_INT)) -+ ret = handle_tx_rx; -+ } -+ /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ -+ writel(intr_status & 0x3FFF, iobase + GETH_INT_STA); -+ -+ return ret; -+} -+ -+void sunxi_start_rx(void *iobase, unsigned long rxbase) -+{ -+ unsigned int value; -+ -+ /* Write the base address of Rx descriptor lists into registers */ -+ writel(rxbase, iobase + GETH_RX_DESC_LIST); -+ -+ value = readl(iobase + GETH_RX_CTL1); -+ value |= 0x40000000; -+ writel(value, iobase + GETH_RX_CTL1); -+} -+ -+void sunxi_stop_rx(void *iobase) -+{ -+ unsigned int value; -+ -+ value = readl(iobase + GETH_RX_CTL1); -+ value &= ~0x40000000; -+ writel(value, iobase + GETH_RX_CTL1); -+} -+ -+void sunxi_start_tx(void *iobase, unsigned long txbase) -+{ -+ unsigned int value; -+ -+ /* Write the base address of Tx descriptor lists into registers */ -+ writel(txbase, iobase + GETH_TX_DESC_LIST); -+ -+ value = readl(iobase + GETH_TX_CTL1); -+ value |= 0x40000000; -+ writel(value, iobase + GETH_TX_CTL1); -+} -+ -+void sunxi_stop_tx(void *iobase) -+{ -+ unsigned int value = readl(iobase + GETH_TX_CTL1); -+ -+ value &= ~0x40000000; -+ writel(value, iobase + GETH_TX_CTL1); -+} -+ -+static int sunxi_dma_init(void *iobase) -+{ -+ unsigned int value; -+ -+ /* Burst should be 8 */ -+ value = (8 << 24); -+ -+#ifdef CONFIG_GMAC_DA -+ value |= RX_TX_PRI; /* Rx has priority over tx */ -+#endif -+ writel(value, iobase + GETH_BASIC_CTL1); -+ -+ /* Mask interrupts by writing to CSR7 */ -+ writel(RX_INT | TX_INT | TX_UNF_INT, iobase + GETH_INT_EN); -+ -+ return 0; -+} -+ -+int sunxi_mac_init(void *iobase, int txmode, int rxmode) -+{ -+ unsigned int value; -+ -+ sunxi_dma_init(iobase); -+ -+ /* Initialize the core component */ -+ value = readl(iobase + GETH_TX_CTL0); -+ value |= (1 << 30); /* Jabber Disable */ -+ writel(value, iobase + GETH_TX_CTL0); -+ -+ value = readl(iobase + GETH_RX_CTL0); -+ value |= (1 << 27); /* Enable CRC & IPv4 Header Checksum */ -+ value |= (1 << 28); /* Automatic Pad/CRC Stripping */ -+ value |= (1 << 29); /* Jumbo Frame Enable */ -+ writel(value, iobase + GETH_RX_CTL0); -+ -+ writel((hwdev.mdc_div << 20), iobase + GETH_MDIO_ADDR); /* MDC_DIV_RATIO */ -+ -+ /* Set the Rx&Tx mode */ -+ value = readl(iobase + GETH_TX_CTL1); -+ if (txmode == SF_DMA_MODE) { -+ /* Transmit COE type 2 cannot be done in cut-through mode. */ -+ value |= TX_MD; -+ /* Operating on second frame increase the performance -+ * especially when transmit store-and-forward is used. -+ */ -+ value |= TX_NEXT_FRM; -+ } else { -+ value &= ~TX_MD; -+ value &= ~TX_TH; -+ /* Set the transmit threshold */ -+ if (txmode <= 64) -+ value |= 0x00000000; -+ else if (txmode <= 128) -+ value |= 0x00000100; -+ else if (txmode <= 192) -+ value |= 0x00000200; -+ else -+ value |= 0x00000300; -+ } -+ writel(value, iobase + GETH_TX_CTL1); -+ -+ value = readl(iobase + GETH_RX_CTL1); -+ if (rxmode == SF_DMA_MODE) { -+ value |= RX_MD; -+ } else { -+ value &= ~RX_MD; -+ value &= ~RX_TH; -+ if (rxmode <= 32) -+ value |= 0x10; -+ else if (rxmode <= 64) -+ value |= 0x00; -+ else if (rxmode <= 96) -+ value |= 0x20; -+ else -+ value |= 0x30; -+ } -+ writel(value, iobase + GETH_RX_CTL1); -+ -+ return 0; -+} -+ -+void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high) -+{ -+ writel(high, iobase + GETH_RX_HASH0); -+ writel(low, iobase + GETH_RX_HASH1); -+} -+ -+void sunxi_set_filter(void *iobase, unsigned long flags) -+{ -+ int tmp_flags; -+ -+ tmp_flags = readl(iobase + GETH_RX_FRM_FLT); -+ -+ tmp_flags |= ((flags >> 31) | -+ ((flags >> 9) & 0x00000002) | -+ ((flags << 1) & 0x00000010) | -+ ((flags >> 3) & 0x00000060) | -+ ((flags << 7) & 0x00000300) | -+ ((flags << 6) & 0x00003000) | -+ ((flags << 12) & 0x00030000) | -+ (flags << 31)); -+ -+ writel(tmp_flags, iobase + GETH_RX_FRM_FLT); -+} -+ -+void sunxi_set_umac(void *iobase, unsigned char *addr, int index) -+{ -+ unsigned long data; -+ -+ data = (addr[5] << 8) | addr[4]; -+ writel(data, iobase + GETH_ADDR_HI(index)); -+ data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; -+ writel(data, iobase + GETH_ADDR_LO(index)); -+} -+ -+void sunxi_mac_enable(void *iobase) -+{ -+ unsigned long value; -+ -+ value = readl(iobase + GETH_TX_CTL0); -+ value |= (1 << 31); -+ writel(value, iobase + GETH_TX_CTL0); -+ -+ value = readl(iobase + GETH_RX_CTL0); -+ value |= (1 << 31); -+ writel(value, iobase + GETH_RX_CTL0); -+} -+ -+void sunxi_mac_disable(void *iobase) -+{ -+ unsigned long value; -+ -+ value = readl(iobase + GETH_TX_CTL0); -+ value &= ~(1 << 31); -+ writel(value, iobase + GETH_TX_CTL0); -+ -+ value = readl(iobase + GETH_RX_CTL0); -+ value &= ~(1 << 31); -+ writel(value, iobase + GETH_RX_CTL0); -+} -+ -+void sunxi_tx_poll(void *iobase) -+{ -+ unsigned int value; -+ -+ value = readl(iobase + GETH_TX_CTL1); -+ writel(value | 0x80000000, iobase + GETH_TX_CTL1); -+} -+ -+void sunxi_rx_poll(void *iobase) -+{ -+ unsigned int value; -+ -+ value = readl(iobase + GETH_RX_CTL1); -+ writel(value | 0x80000000, iobase + GETH_RX_CTL1); -+} -+ -+void sunxi_int_enable(void *iobase) -+{ -+ writel(RX_INT | TX_INT | TX_UNF_INT, iobase + GETH_INT_EN); -+} -+ -+void sunxi_int_disable(void *iobase) -+{ -+ writel(0, iobase + GETH_INT_EN); -+} -+ -+void desc_buf_set(struct dma_desc *desc, unsigned long paddr, int size) -+{ -+ desc->desc1.all &= (~((1 << 11) - 1)); -+ desc->desc1.all |= (size & ((1 << 11) - 1)); -+ desc->desc2 = paddr; -+} -+ -+void desc_set_own(struct dma_desc *desc) -+{ -+ desc->desc0.all |= 0x80000000; -+} -+ -+void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert) -+{ -+ struct dma_desc *desc = first; -+ -+ first->desc1.tx.first_sg = 1; -+ end->desc1.tx.last_seg = 1; -+ end->desc1.tx.interrupt = 1; -+ -+ if (csum_insert) -+ do { -+ desc->desc1.tx.cic = 3; -+ desc++; -+ } while (desc <= end); -+} -+ -+void desc_init(struct dma_desc *desc) -+{ -+ desc->desc1.all = 0; -+ desc->desc2 = 0; -+ -+ desc->desc1.all |= (1 << 24); -+} -+ -+int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x) -+{ -+ int ret = 0; -+ -+ if (desc->desc0.tx.under_err) { -+ x->tx_underflow++; -+ ret = -1; -+ } -+ if (desc->desc0.tx.no_carr) { -+ x->tx_carrier++; -+ ret = -1; -+ } -+ if (desc->desc0.tx.loss_carr) { -+ x->tx_losscarrier++; -+ ret = -1; -+ } -+ -+#if 0 -+ if ((desc->desc0.tx.ex_deferral) || -+ (desc->desc0.tx.ex_coll) || -+ (desc->desc0.tx.late_coll)) -+ stats->collisions += desc->desc0.tx.coll_cnt; -+#endif -+ -+ if (desc->desc0.tx.deferred) -+ x->tx_deferred++; -+ -+ return ret; -+} -+ -+int desc_buf_get_len(struct dma_desc *desc) -+{ -+ return (desc->desc1.all & ((1 << 11) - 1)); -+} -+ -+int desc_buf_get_addr(struct dma_desc *desc) -+{ -+ return desc->desc2; -+} -+ -+int desc_rx_frame_len(struct dma_desc *desc) -+{ -+ return desc->desc0.rx.frm_len; -+} -+ -+int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x) -+{ -+ int ret = good_frame; -+ -+ if (desc->desc0.rx.last_desc == 0) { -+ return discard_frame; -+ } -+ -+ if (desc->desc0.rx.err_sum) { -+ if (desc->desc0.rx.desc_err) -+ x->rx_desc++; -+ -+ if (desc->desc0.rx.sou_filter) -+ x->sa_filter_fail++; -+ -+ if (desc->desc0.rx.over_err) -+ x->overflow_error++; -+ -+ if (desc->desc0.rx.ipch_err) -+ x->ipc_csum_error++; -+ -+ if (desc->desc0.rx.late_coll) -+ x->rx_collision++; -+ -+ if (desc->desc0.rx.crc_err) -+ x->rx_crc++; -+ -+ ret = discard_frame; -+ } -+ -+ if (desc->desc0.rx.len_err) { -+ ret = discard_frame; -+ } -+ if (desc->desc0.rx.mii_err) { -+ ret = discard_frame; -+ } -+ -+ return ret; -+} -+ -+int desc_get_own(struct dma_desc *desc) -+{ -+ return desc->desc0.all & 0x80000000; -+} -+ -+int desc_get_tx_ls(struct dma_desc *desc) -+{ -+ return desc->desc1.tx.last_seg; -+} -+ -+int sunxi_geth_register(void *iobase, int version, unsigned int div) -+{ -+ hwdev.ver = version; -+ hwdev.iobase = iobase; -+ hwdev.mdc_div = div; -+ -+ return 0; -+} -+ -+int sunxi_mac_reset(void *iobase, void (*delay)(int), int n) -+{ -+ unsigned int value; -+ -+ /* DMA SW reset */ -+ value = readl(iobase + GETH_BASIC_CTL1); -+ value |= SOFT_RST; -+ writel(value, iobase + GETH_BASIC_CTL1); -+ -+ delay(n); -+ -+ return !!(readl(iobase + GETH_BASIC_CTL1) & SOFT_RST); -+} -+ -+/** -+ * sunxi_parse_read_str - parse the input string for write attri. -+ * @str: string to be parsed, eg: "0x00 0x01". -+ * @addr: store the reg address. eg: 0x00. -+ * @reg: store the expect value. eg: 0x01. -+ * -+ * return 0 if success, otherwise failed. -+ */ -+int sunxi_parse_read_str(char *str, u16 *addr, u16 *reg) -+{ -+ char *ptr = str; -+ char *tstr = NULL; -+ int ret; -+ -+ /* -+ * Skip the leading whitespace, find the true split symbol. -+ * And it must be 'address value'. -+ */ -+ tstr = strim(str); -+ ptr = strchr(tstr, ' '); -+ if (!ptr) -+ return -EINVAL; -+ -+ /* -+ * Replaced split symbol with a %NUL-terminator temporary. -+ * Will be fixed at end. -+ */ -+ *ptr = '\0'; -+ ret = kstrtos16(tstr, 16, addr); -+ if (ret) -+ goto out; -+ -+ ret = kstrtos16(skip_spaces(ptr + 1), 16, reg); -+ -+out: -+ return ret; -+} -+ -+/** -+ * sunxi_parse_write_str - parse the input string for compare attri. -+ * @str: string to be parsed, eg: "0x00 0x11 0x11". -+ * @addr: store the address. eg: 0x00. -+ * @reg: store the reg. eg: 0x11. -+ * @val: store the value. eg: 0x11. -+ * -+ * return 0 if success, otherwise failed. -+ */ -+int sunxi_parse_write_str(char *str, u16 *addr, -+ u16 *reg, u16 *val) -+{ -+ u16 result_addr[3] = { 0 }; -+ char *ptr = str; -+ char *ptr2 = NULL; -+ int i, ret = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(result_addr); i++) { -+ ptr = skip_spaces(ptr); -+ ptr2 = strchr(ptr, ' '); -+ if (ptr2) -+ *ptr2 = '\0'; -+ -+ ret = kstrtou16(ptr, 16, &result_addr[i]); -+ if (!ptr2) -+ break; -+ -+ *ptr2 = ' '; -+ -+ if (ret) -+ break; -+ -+ ptr = ptr2 + 1; -+ } -+ -+ *addr = result_addr[0]; -+ *reg = result_addr[1]; -+ *val = result_addr[2]; -+ -+ return ret; -+} -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 7fddc8306d82..e907dffd1a5a 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -81,6 +81,14 @@ config AIR_EN8811H_PHY - help - Currently supports the Airoha EN8811H PHY. - -+config AC200_PHY_SUNXI -+ tristate "AC200 EPHY(Sunxi)" -+ depends on NVMEM -+ depends on OF -+ depends on MFD_AC200_SUNXI -+ help -+ Fast ethernet PHY as found in X-Powers AC200(Sunxi) multi-function device. -+ - config AMD_PHY - tristate "AMD and Altima PHYs" - help -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index 202ed7f450da..bfd6209782fa 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -32,6 +32,7 @@ obj-$(CONFIG_SFP) += sfp.o - sfp-obj-$(CONFIG_SFP) += sfp-bus.o - obj-y += $(sfp-obj-y) $(sfp-obj-m) - -+obj-$(CONFIG_AC200_PHY_SUNXI) += sunxi-ephy.o - obj-$(CONFIG_ADIN_PHY) += adin.o - obj-$(CONFIG_ADIN1100_PHY) += adin1100.o - obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o -diff --git a/drivers/net/phy/sunxi-ephy.c b/drivers/net/phy/sunxi-ephy.c -new file mode 100644 -index 000000000000..92f5ba101ced ---- /dev/null -+++ b/drivers/net/phy/sunxi-ephy.c -@@ -0,0 +1,518 @@ -+/* -+ * Copyright © 2015-2016, Shuge -+ * Author: Sugar -+ * -+ * This file is provided under a dual BSD/GPL license. When using or -+ * redistributing this file, you may do so under either 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#define EPHY_AC200 1 -+#define EPHY_AC300 2 -+ -+#define WAIT_MAX_COUNT 200 -+ -+struct ephy_res { -+ struct phy_device *ac300; -+ struct ac200_dev *ac200; -+ atomic_t ephy_en; -+}; -+ -+static struct ephy_res ac200_ephy; -+static struct ephy_res ac300_ephy; -+u8 ephy_type; -+ -+static int ac200_reg_read(struct ac200_dev *ac200, unsigned short reg) -+{ -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(ac200->regmap, reg, &val); -+ -+ if (ret < 0) -+ return ret; -+ else -+ return val; -+} -+ -+static int ac200_reg_write(struct ac200_dev *ac200, unsigned short reg, unsigned short val) -+{ -+ return regmap_write(ac200->regmap, reg, val); -+} -+ -+int ephy_is_enable(void) -+{ -+ if (ephy_type == EPHY_AC200) -+ return atomic_read(&ac200_ephy.ephy_en); -+ else if (ephy_type == EPHY_AC300) -+ return atomic_read(&ac300_ephy.ephy_en); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ephy_is_enable); -+ -+static void disable_intelligent_ieee(struct phy_device *phydev) -+{ -+ unsigned int value; -+ -+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ -+ value = phy_read(phydev, 0x17); /* read address 0 0x17 register */ -+ value &= ~(1 << 3); /* reg 0x17 bit 3, set 0 to disable IEEE */ -+ phy_write(phydev, 0x17, value); -+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ -+} -+ -+static void disable_802_3az_ieee(struct phy_device *phydev) -+{ -+ unsigned int value; -+ -+ phy_write(phydev, 0xd, 0x7); -+ phy_write(phydev, 0xe, 0x3c); -+ phy_write(phydev, 0xd, 0x1 << 14 | 0x7); -+ value = phy_read(phydev, 0xe); -+ value &= ~(0x1 << 1); -+ phy_write(phydev, 0xd, 0x7); -+ phy_write(phydev, 0xe, 0x3c); -+ phy_write(phydev, 0xd, 0x1 << 14 | 0x7); -+ phy_write(phydev, 0xe, value); -+ -+ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ -+ phy_write(phydev, 0x18, 0x0000); -+} -+ -+static void ephy_config_default(struct phy_device *phydev) -+{ -+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ -+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ -+ -+ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ -+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ -+ phy_write(phydev, 0x14, 0x708b); /* PHYAFE TX optimization */ -+ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ -+ phy_write(phydev, 0x15, 0x1530); -+ -+ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 8 */ -+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ -+} -+ -+static void __maybe_unused ephy_config_fixed(struct phy_device *phydev) -+{ -+ phy_write(phydev, 0x1f, 0x0100); /*switch to Page 1 */ -+ phy_write(phydev, 0x12, 0x4824); /*Disable APS */ -+ -+ phy_write(phydev, 0x1f, 0x0200); /*switch to Page 2 */ -+ phy_write(phydev, 0x18, 0x0000); /*PHYAFE TRX optimization */ -+ -+ phy_write(phydev, 0x1f, 0x0600); /*switch to Page 6 */ -+ phy_write(phydev, 0x14, 0x7809); /*PHYAFE TX optimization */ -+ phy_write(phydev, 0x13, 0xf000); /*PHYAFE RX optimization */ -+ phy_write(phydev, 0x10, 0x5523); -+ phy_write(phydev, 0x15, 0x3533); -+ -+ phy_write(phydev, 0x1f, 0x0800); /*switch to Page 8 */ -+ phy_write(phydev, 0x1d, 0x0844); /*disable auto offset */ -+ phy_write(phydev, 0x18, 0x00bc); /*PHYAFE TRX optimization */ -+} -+ -+static void __maybe_unused ephy_config_cali(struct phy_device *phydev, u16 ephy_cali) -+{ -+ int value; -+ value = phy_read(phydev, 0x06); -+ value &= ~(0x0F << 12); -+ value |= (0x0F & (0x03 + ephy_cali)) << 12; -+ phy_write(phydev, 0x06, value); -+ -+ return; -+} -+ -+int ephy_config_init(struct phy_device *phydev) -+{ -+ int value; -+#if 1 //defined(CONFIG_ARCH_SUN50IW9) -+ if (ephy_type == EPHY_AC300) { -+ int ret; -+ u16 ephy_cali = 0; -+ ephy_cali = sun50i_ephy_calibrate_value(); -+ if (ret) { -+ pr_err("ephy cali efuse read fail!\n"); -+ return -1; -+ } -+ ephy_config_cali(ac300_ephy.ac300, ephy_cali); -+ /* -+ * BIT9: the flag of calibration value -+ * 0: Normal -+ * 1: Low level of calibration value -+ */ -+ if (ephy_cali & BIT(9)) { -+ pr_debug("ac300:ephy cali efuse read: fixed!\n"); -+ ephy_config_fixed(phydev); -+ } else { -+ pr_debug("ac300:ephy cali efuse read: default!\n"); -+ ephy_config_default(phydev); -+ } -+ } else { -+ pr_debug("ac200:ephy cali efuse read: default!\n"); -+ ephy_config_default(phydev); -+ } -+#else -+ ephy_config_default(phydev); -+#endif -+ -+#if 0 -+ /* Disable Auto Power Saving mode */ -+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ -+ value = phy_read(phydev, 0x17); -+ value &= ~BIT(13); -+ phy_write(phydev, 0x17, value); -+#endif -+ disable_intelligent_ieee(phydev); /* Disable Intelligent IEEE */ -+ disable_802_3az_ieee(phydev); /* Disable 802.3az IEEE */ -+ phy_write(phydev, 0x1f, 0x0000); /* Switch to Page 0 */ -+ -+#if 1 // def CONFIG_MFD_ACX00 -+ if (ephy_type == EPHY_AC200) { -+ value = ac200_reg_read(ac200_ephy.ac200, AC200_EPHY_CTL); -+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) -+ value |= (1 << 11); -+ else -+ value &= (~(1 << 11)); -+ ac200_reg_write(ac200_ephy.ac200, AC200_EPHY_CTL, value | (1 << 11)); -+ } else -+#endif -+ if (ephy_type == EPHY_AC300) { -+ value = phy_read(ac300_ephy.ac300, 0x06); -+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) -+ value |= (1 << 11); -+ else -+ value &= (~(1 << 11)); -+ phy_write(ac300_ephy.ac300, 0x06, value | (1 << 1)); /*LED_POL 1:Low active*/ -+ } -+ -+#if defined(CONFIG_ARCH_SUN50IW6) -+ value = phy_read(phydev, 0x13); -+ value |= 1 << 12; -+ phy_write(phydev, 0x13, value); -+#endif -+ -+ return 0; -+} -+EXPORT_SYMBOL(ephy_config_init); -+ -+static int ephy_probe(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+#if 0 -+static int ephy_ack_interrupt(struct phy_device *phydev) -+{ -+ int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); -+ -+ if (err < 0) -+ return err; -+ -+ return 0; -+} -+#endif -+ -+ -+static struct phy_driver sunxi_phy_driver = { -+ .phy_id = 0x00441400, -+ .name = "ephy", -+ .phy_id_mask = 0x0ffffff0, -+ .features = PHY_BASIC_FEATURES, //| SUPPORTED_Pause |SUPPORTED_Asym_Pause, -+#if 0 -+ .flags = PHY_HAS_INTERRUPT, -+ .ack_interrupt = ephy_ack_interrupt, -+#endif -+ .config_init = &ephy_config_init, -+ .config_aneg = &genphy_config_aneg, -+ .read_status = &genphy_read_status, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .probe = ephy_probe, -+}; -+ -+static void ac300_ephy_enable(struct ephy_res *priv) -+{ -+ phy_write(priv->ac300, 0x00, 0x1f83); /* release reset */ -+ -+ phy_write(priv->ac300, 0x00, 0x1fb7); /* clk gating (24MHz clock)*/ -+ -+ //phy_write(priv->ac300, 0x05, 0xa81f); -+ phy_write(priv->ac300, 0x05, 0xa819); -+ -+ phy_write(priv->ac300, 0x06, 0x00); -+ -+ msleep(1000); /* FIXME: fix some board compatible issues. */ -+ -+ atomic_set(&ac300_ephy.ephy_en, 1); -+} -+ -+static void ac300_ephy_disable(struct ephy_res *priv) -+{ -+ phy_write(priv->ac300, 0x00, 0x1f40); -+ phy_write(priv->ac300, 0x05, 0xa800); -+ -+ phy_write(priv->ac300, 0x06, 0x01); -+ -+ atomic_set(&ac300_ephy.ephy_en, 0); -+} -+ -+static int ac300_ephy_probe(struct phy_device *phydev) -+{ -+ ac300_ephy.ac300 = phydev; -+ -+ atomic_set(&ac300_ephy.ephy_en, 0); -+ ac300_ephy_enable(&ac300_ephy); -+ -+ ephy_type = EPHY_AC300; -+ -+ return 0; -+} -+ -+static void ac300_ephy_shutdown(struct phy_device *phydev) -+{ -+ ac300_ephy.ac300 = phydev; -+ -+ phy_write(ac300_ephy.ac300, 0x00, 0x1f40); -+ phy_write(ac300_ephy.ac300, 0x05, 0xa800); -+ phy_write(ac300_ephy.ac300, 0x06, 0x01); -+} -+ -+int gmac_ephy_shutdown(void) -+{ -+ if (ephy_type == EPHY_AC300) -+ ac300_ephy_disable(&ac300_ephy); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(gmac_ephy_shutdown); -+ -+static int ac300_ephy_suspend(struct phy_device *phydev) -+{ -+ ac300_ephy_disable(&ac300_ephy); -+ -+ return 0; -+} -+ -+static int ac300_ephy_resume(struct phy_device *phydev) -+{ -+ ac300_ephy_enable(&ac300_ephy); -+ -+ return 0; -+} -+ -+static struct phy_driver ac300_ephy_driver = { -+ .phy_id = 0xc0000000, -+ .name = "ac300", -+ .phy_id_mask = 0xffffffff, -+ .suspend = ac300_ephy_suspend, -+ .resume = ac300_ephy_resume, -+ .probe = ac300_ephy_probe, -+ // .shutdown = ac300_ephy_shutdown, -+}; -+ -+ -+static void ac200_ephy_enable(struct ephy_res *priv) -+{ -+#if 1 //ifdef CONFIG_MFD_ACX00 -+ int value; -+ unsigned char i = 0; -+#if 1 // defined(CONFIG_ARCH_SUN50IW6) || defined(CONFIG_ARCH_SUN50IW9) -+ u16 ephy_cali = 0; -+#endif -+ -+ if (!ac200_enable()) { -+ for (i = 0; i < WAIT_MAX_COUNT; i++) { -+ msleep(10); -+ if (ac200_enable()) -+ break; -+ } -+ if (i == WAIT_MAX_COUNT) { -+ pr_err("acx00 is no enable, and ac200_ephy_enable is fail\n"); -+ return; -+ } -+ } -+ -+ value = ac200_reg_read(priv->ac200, AC200_SYS_EPHY_CTL0); -+ value |= 0x03; -+ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL0, value); -+ value = ac200_reg_read(priv->ac200, AC200_SYS_EPHY_CTL1); -+ -+ value |= 0x0f; -+ -+ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL1, value); -+ ac200_reg_write(priv->ac200, AC200_EPHY_CTL, 0x06); -+ -+ /*for ephy */ -+ value = ac200_reg_read(priv->ac200, AC200_EPHY_CTL); -+ value &= ~(0xf << 12); -+ -+#if 1 // defined(CONFIG_ARCH_SUN50IW6) || defined(CONFIG_ARCH_SUN50IW9) -+ -+ ephy_cali = sun50i_ephy_calibrate_value(); -+ value |= (0x0F & (0x03 + ephy_cali)) << 12; -+#else -+ value |= (0x0F & (0x03 + ac200_reg_read(priv->ac200, AC200_SID))) << 12; -+#endif -+ -+ ac200_reg_write(priv->ac200, AC200_EPHY_CTL, value); -+ -+ atomic_set(&ac200_ephy.ephy_en, 1); -+#endif -+} -+ -+static void ac200_ephy_disable(struct ephy_res *priv) -+{ -+ int value; -+ -+ /* reset ephy */ -+ value = ac200_reg_read(priv->ac200, AC200_SYS_EPHY_CTL0); -+ value &= ~0x01; -+ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL0, value); -+ -+ /* shutdown ephy */ -+ value = ac200_reg_read(priv->ac200, AC200_EPHY_CTL); -+ value |= 0x01; -+ ac200_reg_write(priv->ac200, AC200_EPHY_CTL, value); -+ -+ atomic_set(&priv->ephy_en, 0); -+} -+ -+static const struct platform_device_id ac200_ephy_id[] = { -+ { "ac200-ephy", 0}, -+ { }, -+}; -+MODULE_DEVICE_TABLE(platform, ac200_ephy_id); -+ -+static int ac200_ephy_probe(struct platform_device *pdev) -+{ -+ struct ac200_dev *ax = dev_get_drvdata(pdev->dev.parent); -+ -+ if (!ax) -+ return -ENODEV; -+ -+ ac200_ephy.ac200 = ax; -+ ephy_type = EPHY_AC200; -+ platform_set_drvdata(pdev, &ac200_ephy); -+ -+ atomic_set(&ac200_ephy.ephy_en, 0); -+ ac200_ephy_enable(&ac200_ephy); -+ -+ return 0; -+} -+ -+static int ac200_ephy_remove(struct platform_device *pdev) -+{ -+ ac200_ephy_disable(&ac200_ephy); -+ -+ return 0; -+} -+ -+static int ac200_ephy_suspend(struct device *dev) -+{ -+ ac200_ephy_disable(&ac200_ephy); -+ -+ return 0; -+} -+ -+static int ac200_ephy_resume(struct device *dev) -+{ -+ ac200_ephy_enable(&ac200_ephy); -+ -+ return 0; -+} -+ -+/* Suspend hook structures */ -+static const struct dev_pm_ops ac200_ephy_pm_ops = { -+ .suspend = ac200_ephy_suspend, -+ .resume = ac200_ephy_resume, -+}; -+ -+static struct platform_driver ac200_ephy_driver = { -+ .driver = { -+ .name = "ac200-ephy-sunxi", -+ .owner = THIS_MODULE, -+ .pm = &ac200_ephy_pm_ops, -+ }, -+ .probe = ac200_ephy_probe, -+ .remove = ac200_ephy_remove, -+ .id_table = ac200_ephy_id, -+}; -+ -+static int ephy_init(void) -+{ -+ int ret = 0; -+ -+ ret = platform_driver_register(&ac200_ephy_driver); -+ if (ret) -+ return ret; -+ -+ ret = phy_driver_register(&ac300_ephy_driver, THIS_MODULE); -+ if (ret) -+ goto ac300_ephy_error; -+ -+ ret = phy_driver_register(&sunxi_phy_driver, THIS_MODULE); -+ if (ret) -+ goto ephy_driver_error; -+ -+ return ret; -+ -+ephy_driver_error: -+ phy_driver_unregister(&ac300_ephy_driver); -+ -+ac300_ephy_error: -+ platform_driver_unregister(&ac200_ephy_driver); -+ -+ return ret; -+} -+ -+static void ephy_exit(void) -+{ -+ phy_driver_unregister(&sunxi_phy_driver); -+ phy_driver_unregister(&ac300_ephy_driver); -+ platform_driver_unregister(&ac200_ephy_driver); -+} -+ -+module_init(ephy_init); -+module_exit(ephy_exit); -+ -+static struct mdio_device_id __maybe_unused ephy_tbl[] = { -+ { 0x00441400, 0x0ffffff0 }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(mdio, ephy_tbl); -+ -+MODULE_DESCRIPTION("Allwinner EPHY drivers"); -+MODULE_AUTHOR("Sugar "); -+MODULE_LICENSE("GPL"); -diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h -new file mode 100644 -index 000000000000..84d89cbfbd3d ---- /dev/null -+++ b/include/linux/mfd/ac200.h -@@ -0,0 +1,213 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * AC200 register list -+ * -+ * Copyright (C) 2019 Jernej Skrabec -+ */ -+ -+#ifndef __LINUX_MFD_AC200_H -+#define __LINUX_MFD_AC200_H -+ -+#include -+#include -+ -+/* interface registers (can be accessed from any page) */ -+#define AC200_TWI_CHANGE_TO_RSB 0x3E -+#define AC200_TWI_PAD_DELAY 0xC4 -+#define AC200_TWI_REG_ADDR_H 0xFE -+ -+/* General registers */ -+#define AC200_SYS_VERSION 0x0000 -+#define AC200_SYS_CONTROL 0x0002 -+#define AC200_SYS_IRQ_ENABLE 0x0004 -+#define AC200_SYS_IRQ_STATUS 0x0006 -+#define AC200_SYS_CLK_CTL 0x0008 -+#define AC200_SYS_DLDO_OSC_CTL 0x000A -+#define AC200_SYS_PLL_CTL0 0x000C -+#define AC200_SYS_PLL_CTL1 0x000E -+#define AC200_SYS_AUDIO_CTL0 0x0010 -+#define AC200_SYS_AUDIO_CTL1 0x0012 -+#define AC200_SYS_EPHY_CTL0 0x0014 -+#define AC200_SYS_EPHY_CTL1 0x0016 -+#define AC200_SYS_TVE_CTL0 0x0018 -+#define AC200_SYS_TVE_CTL1 0x001A -+ -+/* Audio Codec registers */ -+#define AC200_AC_SYS_CLK_CTL 0x2000 -+#define AC200_SYS_MOD_RST 0x2002 -+#define AC200_SYS_SAMP_CTL 0x2004 -+#define AC200_I2S_CTL 0x2100 -+#define AC200_I2S_CLK 0x2102 -+#define AC200_I2S_FMT0 0x2104 -+#define AC200_I2S_FMT1 0x2108 -+#define AC200_I2S_MIX_SRC 0x2114 -+#define AC200_I2S_MIX_GAIN 0x2116 -+#define AC200_I2S_DACDAT_DVC 0x2118 -+#define AC200_I2S_ADCDAT_DVC 0x211A -+#define AC200_AC_DAC_DPC 0x2200 -+#define AC200_AC_DAC_MIX_SRC 0x2202 -+#define AC200_AC_DAC_MIX_GAIN 0x2204 -+#define AC200_DACA_OMIXER_CTRL 0x2220 -+#define AC200_OMIXER_SR 0x2222 -+#define AC200_LINEOUT_CTRL 0x2224 -+#define AC200_AC_ADC_DPC 0x2300 -+#define AC200_MBIAS_CTRL 0x2310 -+#define AC200_ADC_MIC_CTRL 0x2320 -+#define AC200_ADCMIXER_SR 0x2322 -+#define AC200_ANALOG_TUNING0 0x232A -+#define AC200_ANALOG_TUNING1 0x232C -+#define AC200_AC_AGC_SEL 0x2480 -+#define AC200_ADC_DAPLCTRL 0x2500 -+#define AC200_ADC_DAPRCTRL 0x2502 -+#define AC200_ADC_DAPLSTA 0x2504 -+#define AC200_ADC_DAPRSTA 0x2506 -+#define AC200_ADC_DAPLTL 0x2508 -+#define AC200_ADC_DAPRTL 0x250A -+#define AC200_ADC_DAPLHAC 0x250C -+#define AC200_ADC_DAPLLAC 0x250E -+#define AC200_ADC_DAPRHAC 0x2510 -+#define AC200_ADC_DAPRLAC 0x2512 -+#define AC200_ADC_DAPLDT 0x2514 -+#define AC200_ADC_DAPLAT 0x2516 -+#define AC200_ADC_DAPRDT 0x2518 -+#define AC200_ADC_DAPRAT 0x251A -+#define AC200_ADC_DAPNTH 0x251C -+#define AC200_ADC_DAPLHNAC 0x251E -+#define AC200_ADC_DAPLLNAC 0x2520 -+#define AC200_ADC_DAPRHNAC 0x2522 -+#define AC200_ADC_DAPRLNAC 0x2524 -+#define AC200_AC_DAPHHPFC 0x2526 -+#define AC200_AC_DAPLHPFC 0x2528 -+#define AC200_AC_DAPOPT 0x252A -+#define AC200_AC_DAC_DAPCTRL 0x3000 -+#define AC200_AC_DRC_HHPFC 0x3002 -+#define AC200_AC_DRC_LHPFC 0x3004 -+#define AC200_AC_DRC_CTRL 0x3006 -+#define AC200_AC_DRC_LPFHAT 0x3008 -+#define AC200_AC_DRC_LPFLAT 0x300A -+#define AC200_AC_DRC_RPFHAT 0x300C -+#define AC200_AC_DRC_RPFLAT 0x300E -+#define AC200_AC_DRC_LPFHRT 0x3010 -+#define AC200_AC_DRC_LPFLRT 0x3012 -+#define AC200_AC_DRC_RPFHRT 0x3014 -+#define AC200_AC_DRC_RPFLRT 0x3016 -+#define AC200_AC_DRC_LRMSHAT 0x3018 -+#define AC200_AC_DRC_LRMSLAT 0x301A -+#define AC200_AC_DRC_RRMSHAT 0x301C -+#define AC200_AC_DRC_RRMSLAT 0x301E -+#define AC200_AC_DRC_HCT 0x3020 -+#define AC200_AC_DRC_LCT 0x3022 -+#define AC200_AC_DRC_HKC 0x3024 -+#define AC200_AC_DRC_LKC 0x3026 -+#define AC200_AC_DRC_HOPC 0x3028 -+#define AC200_AC_DRC_LOPC 0x302A -+#define AC200_AC_DRC_HLT 0x302C -+#define AC200_AC_DRC_LLT 0x302E -+#define AC200_AC_DRC_HKI 0x3030 -+#define AC200_AC_DRC_LKI 0x3032 -+#define AC200_AC_DRC_HOPL 0x3034 -+#define AC200_AC_DRC_LOPL 0x3036 -+#define AC200_AC_DRC_HET 0x3038 -+#define AC200_AC_DRC_LET 0x303A -+#define AC200_AC_DRC_HKE 0x303C -+#define AC200_AC_DRC_LKE 0x303E -+#define AC200_AC_DRC_HOPE 0x3040 -+#define AC200_AC_DRC_LOPE 0x3042 -+#define AC200_AC_DRC_HKN 0x3044 -+#define AC200_AC_DRC_LKN 0x3046 -+#define AC200_AC_DRC_SFHAT 0x3048 -+#define AC200_AC_DRC_SFLAT 0x304A -+#define AC200_AC_DRC_SFHRT 0x304C -+#define AC200_AC_DRC_SFLRT 0x304E -+#define AC200_AC_DRC_MXGHS 0x3050 -+#define AC200_AC_DRC_MXGLS 0x3052 -+#define AC200_AC_DRC_MNGHS 0x3054 -+#define AC200_AC_DRC_MNGLS 0x3056 -+#define AC200_AC_DRC_EPSHC 0x3058 -+#define AC200_AC_DRC_EPSLC 0x305A -+#define AC200_AC_DRC_HPFHGAIN 0x305E -+#define AC200_AC_DRC_HPFLGAIN 0x3060 -+#define AC200_AC_DRC_BISTCR 0x3100 -+#define AC200_AC_DRC_BISTST 0x3102 -+ -+/* TVE registers */ -+#define AC200_TVE_CTL0 0x4000 -+#define AC200_TVE_CTL1 0x4002 -+#define AC200_TVE_MOD0 0x4004 -+#define AC200_TVE_MOD1 0x4006 -+#define AC200_TVE_DAC_CFG0 0x4008 -+#define AC200_TVE_DAC_CFG1 0x400A -+#define AC200_TVE_YC_DELAY 0x400C -+#define AC200_TVE_YC_FILTER 0x400E -+#define AC200_TVE_BURST_FRQ0 0x4010 -+#define AC200_TVE_BURST_FRQ1 0x4012 -+#define AC200_TVE_FRONT_PORCH 0x4014 -+#define AC200_TVE_BACK_PORCH 0x4016 -+#define AC200_TVE_TOTAL_LINE 0x401C -+#define AC200_TVE_FIRST_ACTIVE 0x401E -+#define AC200_TVE_BLACK_LEVEL 0x4020 -+#define AC200_TVE_BLANK_LEVEL 0x4022 -+#define AC200_TVE_PLUG_EN 0x4030 -+#define AC200_TVE_PLUG_IRQ_EN 0x4032 -+#define AC200_TVE_PLUG_IRQ_STA 0x4034 -+#define AC200_TVE_PLUG_STA 0x4038 -+#define AC200_TVE_PLUG_DEBOUNCE 0x4040 -+#define AC200_TVE_DAC_TEST 0x4042 -+#define AC200_TVE_PLUG_PULSE_LEVEL 0x40F4 -+#define AC200_TVE_PLUG_PULSE_START 0x40F8 -+#define AC200_TVE_PLUG_PULSE_PERIOD 0x40FA -+#define AC200_TVE_IF_CTL 0x5000 -+#define AC200_TVE_IF_TIM0 0x5008 -+#define AC200_TVE_IF_TIM1 0x500A -+#define AC200_TVE_IF_TIM2 0x500C -+#define AC200_TVE_IF_TIM3 0x500E -+#define AC200_TVE_IF_SYNC0 0x5010 -+#define AC200_TVE_IF_SYNC1 0x5012 -+#define AC200_TVE_IF_SYNC2 0x5014 -+#define AC200_TVE_IF_TIM4 0x5016 -+#define AC200_TVE_IF_STATUS 0x5018 -+ -+/* EPHY registers */ -+#define AC200_EPHY_CTL 0x6000 -+#define AC200_EPHY_BIST 0x6002 -+ -+/* eFuse registers (0x8000 - 0x9FFF, layout unknown) */ -+ -+/* RTC registers */ -+#define AC200_LOSC_CTRL0 0xA000 -+#define AC200_LOSC_CTRL1 0xA002 -+#define AC200_LOSC_AUTO_SWT_STA 0xA004 -+#define AC200_INTOSC_CLK_PRESCAL 0xA008 -+#define AC200_RTC_YY_MM_DD0 0xA010 -+#define AC200_RTC_YY_MM_DD1 0xA012 -+#define AC200_RTC_HH_MM_SS0 0xA014 -+#define AC200_RTC_HH_MM_SS1 0xA016 -+#define AC200_ALARM0_CUR_VLU0 0xA024 -+#define AC200_ALARM0_CUR_VLU1 0xA026 -+#define AC200_ALARM0_ENABLE 0xA028 -+#define AC200_ALARM0_IRQ_EN 0xA02C -+#define AC200_ALARM0_IRQ_STA 0xA030 -+#define AC200_ALARM1_WK_HH_MM_SS0 0xA040 -+#define AC200_ALARM1_WK_HH_MM_SS1 0xA042 -+#define AC200_ALARM1_ENABLE 0xA044 -+#define AC200_ALARM1_IRQ_EN 0xA048 -+#define AC200_ALARM1_IRQ_STA 0xA04C -+#define AC200_ALARM_CONFIG 0xA050 -+#define AC200_LOSC_OUT_GATING 0xA060 -+#define AC200_GP_DATA(x) (0xA100 + (x) * 2) -+#define AC200_RTC_DEB 0xA170 -+#define AC200_GPL_HOLD_OUTPUT 0xA180 -+#define AC200_VDD_RTC 0xA190 -+#define AC200_IC_CHARA0 0xA1F0 -+#define AC200_IC_CHARA1 0xA1F2 -+ -+struct ac200_dev { -+ struct clk *clk; -+ struct regmap *regmap; -+ struct regmap_irq_chip_data *regmap_irqc; -+}; -+ -+extern int ac200_enable(void); -+extern uint16_t sun50i_ephy_calibrate_value(void); -+ -+#endif /* __LINUX_MFD_AC200_H */ -diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h -index d0f66a5e1b2a..71349cac1a67 100644 ---- a/include/linux/of_gpio.h -+++ b/include/linux/of_gpio.h -@@ -15,6 +15,21 @@ - #include /* FIXME: Shouldn't be here */ - #include - -+/* -+ * This is Linux-specific flags. By default controllers' and Linux' mapping -+ * match, but GPIO controllers are free to translate their own flags to -+ * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. -+ */ -+enum of_gpio_flags { -+ OF_GPIO_ACTIVE_LOW = 0x1, -+ OF_GPIO_SINGLE_ENDED = 0x2, -+ OF_GPIO_OPEN_DRAIN = 0x4, -+ OF_GPIO_TRANSITORY = 0x8, -+ OF_GPIO_PULL_UP = 0x10, -+ OF_GPIO_PULL_DOWN = 0x20, -+ OF_GPIO_PULL_DISABLE = 0x40, -+}; -+ - struct device_node; - - #ifdef CONFIG_OF_GPIO -@@ -22,6 +37,9 @@ struct device_node; - extern int of_get_named_gpio(const struct device_node *np, - const char *list_name, int index); - -+extern int of_get_named_gpio_flags(const struct device_node *np, -+ const char *list_name, int index, enum of_gpio_flags *flags); -+ - #else /* CONFIG_OF_GPIO */ - - #include --- -GitLab diff --git a/patch/kernel/archive/sunxi-6.10/patches.megous/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch b/patch/kernel/archive/sunxi-6.10/patches.megous/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch deleted file mode 100644 index 6fdd5c5e6f89..000000000000 --- a/patch/kernel/archive/sunxi-6.10/patches.megous/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch +++ /dev/null @@ -1,1313 +0,0 @@ -From 0fb15c313712ddb6bcd3f1c5a731524f43f1dd6c Mon Sep 17 00:00:00 2001 -From: chraac -Date: Thu, 15 Aug 2024 23:38:44 +0800 -Subject: drivers: pwm: Add pwm-sunxi-enhance driver for h616 - ---- - drivers/pwm/Kconfig | 9 + - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-sunxi-enhance.c | 1193 +++++++++++++++++++++++++++++++ - drivers/pwm/pwm-sunxi-enhance.h | 60 ++ - 4 files changed, 1263 insertions(+) - create mode 100644 drivers/pwm/pwm-sunxi-enhance.c - create mode 100644 drivers/pwm/pwm-sunxi-enhance.h - -diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig -index 1dd7921194f5..1265855872fe 100644 ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -621,6 +621,15 @@ config PWM_SUN4I - To compile this driver as a module, choose M here: the module - will be called pwm-sun4i. - -+config PWM_SUNXI_ENHANCE -+ tristate "Sunxi Enhance PWM support" -+ depends on PWM && ARCH_SUNXI -+ help -+ Enhance PWM framework driver for sunxi. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pwm-sunxi. -+ - config PWM_SUNPLUS - tristate "Sunplus PWM support" - depends on ARCH_SUNPLUS || COMPILE_TEST -diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile -index 90913519f11a..ff177f6b73b6 100644 ---- a/drivers/pwm/Makefile -+++ b/drivers/pwm/Makefile -@@ -57,6 +57,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o - obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o - obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o - obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o -+obj-$(CONFIG_PWM_SUNXI_ENHANCE) += pwm-sunxi-enhance.o - obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o - obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o - obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o -diff --git a/drivers/pwm/pwm-sunxi-enhance.c b/drivers/pwm/pwm-sunxi-enhance.c -new file mode 100644 -index 000000000000..70e6005a160c ---- /dev/null -+++ b/drivers/pwm/pwm-sunxi-enhance.c -@@ -0,0 +1,1193 @@ -+/* -+ * Allwinnertech pulse-width-modulation controller driver -+ * -+ * Copyright (C) 2015 AllWinner -+ * -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "pwm-sunxi-enhance.h" -+ -+#define PWM_NUM_MAX 4 -+#define PWM_BIND_NUM 2 -+#define PWM_PIN_STATE_ACTIVE "default" -+//#define PWM_PIN_STATE_SLEEP "sleep" -+ -+#define SETMASK(width, shift) ((width ? ((-1U) >> (32 - width)) : 0) << (shift)) -+#define CLRMASK(width, shift) (~(SETMASK(width, shift))) -+#define GET_BITS(shift, width, reg) \ -+ (((reg)&SETMASK(width, shift)) >> (shift)) -+#define SET_BITS(shift, width, reg, val) \ -+ (((reg)&CLRMASK(width, shift)) | (val << (shift))) -+ -+static int pwm_debug = 0; -+module_param(pwm_debug, int, 0644); -+MODULE_PARM_DESC(pwm_debug, "enable pwm debug"); -+ -+#define pwm_debug(fmt, ...) \ -+ if (pwm_debug) \ -+ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) -+ -+struct sunxi_pwm_config -+{ -+ unsigned int dead_time; -+ unsigned int bind_pwm; -+ -+ unsigned int clk_bypass_output; -+}; -+ -+struct sunxi_pwm_chip -+{ -+ struct pwm_chip chip; -+ void __iomem *base; -+ struct sunxi_pwm_config *config; -+ struct clk *bus_clk; -+ struct clk *clk; -+ struct reset_control *pwm_rst_clk; -+}; -+ -+static inline struct sunxi_pwm_chip *to_sunxi_pwm_chip(struct pwm_chip *chip) -+{ -+ return container_of(chip, struct sunxi_pwm_chip, chip); -+} -+ -+static inline u32 sunxi_pwm_readl(struct pwm_chip *chip, u32 offset) -+{ -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ u32 value = 0; -+ -+ value = readl(pc->base + offset); -+ -+ return value; -+} -+ -+static inline u32 sunxi_pwm_writel(struct pwm_chip *chip, u32 offset, u32 value) -+{ -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ writel(value, pc->base + offset); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_pin_set_state(struct device *dev, char *name) -+{ -+ struct pinctrl *pctl; -+ struct pinctrl_state *state; -+ int ret = -1; -+ -+ pctl = devm_pinctrl_get(dev); -+ if (IS_ERR(pctl)) -+ { -+ dev_err(dev, "pinctrl_get failed!\n"); -+ ret = PTR_ERR(pctl); -+ goto exit; -+ } -+ -+ state = pinctrl_lookup_state(pctl, name); -+ if (IS_ERR(state)) -+ { -+ dev_err(dev, "pinctrl_lookup_state(%s) failed!\n", name); -+ ret = PTR_ERR(state); -+ goto exit; -+ } -+ -+ ret = pinctrl_select_state(pctl, state); -+ if (ret < 0) -+ { -+ dev_err(dev, "pinctrl_select_state(%s) failed!\n", name); -+ goto exit; -+ } -+ ret = 0; -+ -+exit: -+ return ret; -+} -+ -+static int sunxi_pwm_get_config(struct platform_device *pdev, struct sunxi_pwm_config *config) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ int ret = 0; -+ -+ /* read register config */ -+ ret = of_property_read_u32(np, "bind_pwm", &config->bind_pwm); -+ if (ret < 0) -+ { -+ /*if there is no bind pwm,set 255, dual pwm invalid!*/ -+ config->bind_pwm = 255; -+ ret = 0; -+ } -+ -+ ret = of_property_read_u32(np, "dead_time", &config->dead_time); -+ if (ret < 0) -+ { -+ /*if there is bind pwm, but not set dead time,set bind pwm 255,dual pwm invalid!*/ -+ config->bind_pwm = 255; -+ ret = 0; -+ } -+ -+ ret = of_property_read_u32(np, "clk_bypass_output", &config->clk_bypass_output); -+ if (ret < 0) -+ { -+ /*if use pwm as the internal clock source!*/ -+ config->clk_bypass_output = 0; -+ ret = 0; -+ } -+ -+ of_node_put(np); -+ -+ return ret; -+} -+ -+static int sunxi_pwm_set_polarity_single(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) -+{ -+ u32 temp; -+ unsigned int reg_offset, reg_shift, reg_width; -+ u32 sel = 0; -+ -+ sel = pwm->hwpwm; -+ reg_offset = PWM_PCR_BASE + sel * 0x20; -+ reg_shift = PWM_ACT_STA_SHIFT; -+ reg_width = PWM_ACT_STA_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ if (polarity == PWM_POLARITY_NORMAL) -+ temp = SET_BITS(reg_shift, 1, temp, 1); -+ else -+ temp = SET_BITS(reg_shift, 1, temp, 0); -+ -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_set_polarity_dual(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity, int bind_num) -+{ -+ u32 temp[2]; -+ unsigned int reg_offset[2], reg_shift[2], reg_width[2]; -+ u32 sel[2] = {0}; -+ -+ sel[0] = pwm->hwpwm; -+ sel[1] = bind_num - (int)chip->id; -+ /* config current pwm*/ -+ reg_offset[0] = PWM_PCR_BASE + sel[0] * 0x20; -+ reg_shift[0] = PWM_ACT_STA_SHIFT; -+ reg_width[0] = PWM_ACT_STA_WIDTH; -+ temp[0] = sunxi_pwm_readl(chip, reg_offset[0]); -+ if (polarity == PWM_POLARITY_NORMAL) -+ temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 1); -+ else -+ temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 0); -+ -+ /* config bind pwm*/ -+ reg_offset[1] = PWM_PCR_BASE + sel[1] * 0x20; -+ reg_shift[1] = PWM_ACT_STA_SHIFT; -+ reg_width[1] = PWM_ACT_STA_WIDTH; -+ temp[1] = sunxi_pwm_readl(chip, reg_offset[1]); -+ -+ /*bind pwm's polarity is reverse compare with the current pwm*/ -+ if (polarity == PWM_POLARITY_NORMAL) -+ temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 0); -+ else -+ temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 1); -+ -+ /*config register at the same time*/ -+ sunxi_pwm_writel(chip, reg_offset[0], temp[0]); -+ sunxi_pwm_writel(chip, reg_offset[1], temp[1]); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) -+{ -+ int bind_num; -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ bind_num = pc->config[pwm->hwpwm].bind_pwm; -+ if (bind_num == 255) -+ sunxi_pwm_set_polarity_single(chip, pwm, polarity); -+ else -+ sunxi_pwm_set_polarity_dual(chip, pwm, polarity, bind_num); -+ -+ return 0; -+} -+ -+static u32 get_pccr_reg_offset(u32 sel, u32 *reg_offset) -+{ -+ switch (sel) -+ { -+ case 0: -+ case 1: -+ *reg_offset = PWM_PCCR01; -+ break; -+ case 2: -+ case 3: -+ *reg_offset = PWM_PCCR23; -+ break; -+ case 4: -+ case 5: -+ *reg_offset = PWM_PCCR45; -+ break; -+ case 6: -+ case 7: -+ *reg_offset = PWM_PCCR67; -+ break; -+ case 8: -+ *reg_offset = PWM_PCCR8; -+ break; -+ default: -+ pr_err("%s:Not supported!\n", __func__); -+ break; -+ } -+ return 0; -+} -+ -+static u32 get_pdzcr_reg_offset(u32 sel, u32 *reg_offset) -+{ -+ switch (sel) -+ { -+ case 0: -+ case 1: -+ *reg_offset = PWM_PDZCR01; -+ break; -+ case 2: -+ case 3: -+ *reg_offset = PWM_PDZCR23; -+ break; -+ case 4: -+ case 5: -+ *reg_offset = PWM_PDZCR45; -+ break; -+ case 6: -+ case 7: -+ *reg_offset = PWM_PDZCR67; -+ break; -+ default: -+ pr_err("%s:Not supported!\n", __func__); -+ break; -+ } -+ return 0; -+} -+ -+#define PRESCALE_MAX 256 -+ -+static int sunxi_pwm_config_single(struct pwm_chip *chip, struct pwm_device *pwm, -+ int duty_ns, int period_ns) -+{ -+ unsigned int temp; -+ unsigned long long c = 0; -+ unsigned long entire_cycles = 256, active_cycles = 192; -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ unsigned int reg_offset, reg_shift, reg_width; -+ unsigned int reg_bypass_shift; -+ unsigned int reg_clk_src_shift, reg_clk_src_width; -+ unsigned int reg_div_m_shift, reg_div_m_width; -+ unsigned int pre_scal_id = 0, div_m = 0, prescale = 0; -+ u32 sel = 0; -+ u32 pre_scal[][2] = { -+ -+ /* reg_value clk_pre_div */ -+ {0, 1}, -+ {1, 2}, -+ {2, 4}, -+ {3, 8}, -+ {4, 16}, -+ {5, 32}, -+ {6, 64}, -+ {7, 128}, -+ {8, 256}, -+ }; -+ -+ sel = pwm->hwpwm; -+ -+ get_pccr_reg_offset(sel, ®_offset); -+ if ((sel % 2) == 0) -+ reg_bypass_shift = 0x5; -+ else -+ reg_bypass_shift = 0x6; -+ /*src clk reg*/ -+ reg_clk_src_shift = PWM_CLK_SRC_SHIFT; -+ reg_clk_src_width = PWM_CLK_SRC_WIDTH; -+ -+ if (period_ns > 0 && period_ns <= 10) -+ { -+ /* if freq lt 100M, then direct output 100M clock,set by pass. */ -+ c = 100000000; -+ /*bypass reg*/ -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_bypass_shift, 1, temp, 1); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ /*clk_src_reg*/ -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ return 0; -+ } -+ else if (period_ns > 10 && period_ns <= 334) -+ { -+ /* if freq between 3M~100M, then select 100M as clock */ -+ c = 100000000; -+ /*set clk bypass_output reg to 1 when pwm is used as the internal clock source.*/ -+ if (pc->config[pwm->hwpwm].clk_bypass_output == 1) { -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_bypass_shift, 1, temp, 1); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ } -+ /*clk_src_reg*/ -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ } -+ else if (period_ns > 334) -+ { -+ /* if freq < 3M, then select 24M clock */ -+ c = 24000000; -+ /*set clk bypass_output reg to 1 when pwm is used as the internal clock source.*/ -+ if (pc->config[pwm->hwpwm].clk_bypass_output == 1) { -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_bypass_shift, 1, temp, 1); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ } -+ /*clk_src_reg*/ -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ } -+ pwm_debug("duty_ns=%d period_ns=%d c =%llu.\n", duty_ns, period_ns, c); -+ -+ c = c * period_ns; -+ do_div(c, 1000000000); -+ entire_cycles = (unsigned long)c; -+ -+ for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++) -+ { -+ if (entire_cycles <= 65536) -+ break; -+ for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++) -+ { -+ entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1); -+ if (entire_cycles <= 65536) -+ { -+ div_m = pre_scal[pre_scal_id][0]; -+ break; -+ } -+ } -+ } -+ -+ c = (unsigned long long)entire_cycles * duty_ns; -+ do_div(c, period_ns); -+ active_cycles = c; -+ if (entire_cycles == 0) -+ entire_cycles++; -+ -+ /* config clk div_m*/ -+ reg_div_m_shift = PWM_DIV_M_SHIFT; -+ reg_div_m_width = PWM_DIV_M_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ /* config prescal */ -+ reg_offset = PWM_PCR_BASE + 0x20 * sel; -+ reg_shift = PWM_PRESCAL_SHIFT; -+ reg_width = PWM_PRESCAL_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_shift, reg_width, temp, prescale); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ /* config active cycles */ -+ reg_offset = PWM_PPR_BASE + 0x20 * sel; -+ reg_shift = PWM_ACT_CYCLES_SHIFT; -+ reg_width = PWM_ACT_CYCLES_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_shift, reg_width, temp, active_cycles); -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ /* config period cycles */ -+ reg_offset = PWM_PPR_BASE + 0x20 * sel; -+ reg_shift = PWM_PERIOD_CYCLES_SHIFT; -+ reg_width = PWM_PERIOD_CYCLES_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset); -+ temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1)); -+ -+ sunxi_pwm_writel(chip, reg_offset, temp); -+ -+ pwm_debug("active_cycles=%lu entire_cycles=%lu prescale=%u div_m=%u\n", -+ active_cycles, entire_cycles, prescale, div_m); -+ return 0; -+} -+ -+static int sunxi_pwm_config_dual(struct pwm_chip *chip, struct pwm_device *pwm, -+ int duty_ns, int period_ns, int bind_num) -+{ -+ u32 value[2] = {0}; -+ unsigned int temp; -+ unsigned long long c = 0, clk = 0, clk_temp = 0; -+ unsigned long entire_cycles = 256, active_cycles = 192; -+ unsigned int reg_offset[2], reg_shift[2], reg_width[2]; -+ unsigned int reg_bypass_shift; -+ unsigned int reg_dz_en_offset[2], reg_dz_en_shift[2], reg_dz_en_width[2]; -+ unsigned int pre_scal_id = 0, div_m = 0, prescale = 0; -+ int src_clk_sel = 0; -+ int i = 0; -+ unsigned int dead_time = 0, duty = 0; -+ u32 pre_scal[][2] = { -+ -+ /* reg_value clk_pre_div */ -+ {0, 1}, -+ {1, 2}, -+ {2, 4}, -+ {3, 8}, -+ {4, 16}, -+ {5, 32}, -+ {6, 64}, -+ {7, 128}, -+ {8, 256}, -+ }; -+ unsigned int pwm_index[2] = {0}; -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ pwm_index[0] = pwm->hwpwm; -+ pwm_index[1] = bind_num - (int)chip->id; -+ -+ /* if duty time < dead time,it is wrong. */ -+ dead_time = pc->config[pwm_index[0]].dead_time; -+ duty = (unsigned int)duty_ns; -+ /* judge if the pwm eanble dead zone */ -+ get_pdzcr_reg_offset(pwm_index[0], ®_dz_en_offset[0]); -+ reg_dz_en_shift[0] = PWM_DZ_EN_SHIFT; -+ reg_dz_en_width[0] = PWM_DZ_EN_WIDTH; -+ -+ value[0] = sunxi_pwm_readl(chip, reg_dz_en_offset[0]); -+ value[0] = SET_BITS(reg_dz_en_shift[0], reg_dz_en_width[0], value[0], 1); -+ sunxi_pwm_writel(chip, reg_dz_en_offset[0], value[0]); -+ temp = sunxi_pwm_readl(chip, reg_dz_en_offset[0]); -+ temp &= (1u << reg_dz_en_shift[0]); -+ if (duty < dead_time || temp == 0) -+ { -+ pr_err("[PWM]duty time or dead zone error.\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ if ((i % 2) == 0) -+ reg_bypass_shift = 0x5; -+ else -+ reg_bypass_shift = 0x6; -+ get_pccr_reg_offset(pwm_index[i], ®_offset[i]); -+ reg_shift[i] = reg_bypass_shift; -+ reg_width[i] = PWM_BYPASS_WIDTH; -+ } -+ -+ if (period_ns > 0 && period_ns <= 10) -+ { -+ /* if freq lt 100M, then direct output 100M clock,set by pass */ -+ clk = 100000000; -+ src_clk_sel = 1; -+ -+ /* config the two pwm bypass */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ -+ reg_shift[i] = PWM_CLK_SRC_SHIFT; -+ reg_width[i] = PWM_CLK_SRC_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ return 0; -+ } -+ else if (period_ns > 10 && period_ns <= 334) -+ { -+ clk = 100000000; -+ src_clk_sel = 1; -+ } -+ else if (period_ns > 334) -+ { -+ /* if freq < 3M, then select 24M clock */ -+ clk = 24000000; -+ src_clk_sel = 0; -+ } -+ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_shift[i] = PWM_CLK_SRC_SHIFT; -+ reg_width[i] = PWM_CLK_SRC_WIDTH; -+ -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, src_clk_sel); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ c = clk; -+ c *= period_ns; -+ do_div(c, 1000000000); -+ entire_cycles = (unsigned long)c; -+ -+ /* get div_m and prescale,which satisfy: deat_val <= 256, entire <= 65536 */ -+ for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++) -+ { -+ for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++) -+ { -+ entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1); -+ clk_temp = clk; -+ do_div(clk_temp, pre_scal[pre_scal_id][1] * (prescale + 1)); -+ clk_temp *= dead_time; -+ do_div(clk_temp, 1000000000); -+ if (entire_cycles <= 65536 && clk_temp <= 256) -+ { -+ div_m = pre_scal[pre_scal_id][0]; -+ break; -+ } -+ } -+ if (entire_cycles <= 65536 && clk_temp <= 256) -+ break; -+ else -+ { -+ pr_err("%s:config dual err.entire_cycles=%lu, dead_zone_val=%llu", -+ __func__, entire_cycles, clk_temp); -+ return -EINVAL; -+ } -+ } -+ -+ c = (unsigned long long)entire_cycles * duty_ns; -+ do_div(c, period_ns); -+ active_cycles = c; -+ if (entire_cycles == 0) -+ entire_cycles++; -+ -+ /* config clk div_m*/ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_shift[i] = PWM_DIV_M_SHIFT; -+ reg_width[i] = PWM_DIV_M_SHIFT; -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, div_m); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ /* config prescal */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_offset[i] = PWM_PCR_BASE + 0x20 * pwm_index[i]; -+ reg_shift[i] = PWM_PRESCAL_SHIFT; -+ reg_width[i] = PWM_PRESCAL_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, prescale); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ /* config active cycles */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_offset[i] = PWM_PPR_BASE + 0x20 * pwm_index[i]; -+ reg_shift[i] = PWM_ACT_CYCLES_SHIFT; -+ reg_width[i] = PWM_ACT_CYCLES_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, active_cycles); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ /* config period cycles */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_offset[i] = PWM_PPR_BASE + 0x20 * pwm_index[i]; -+ reg_shift[i] = PWM_PERIOD_CYCLES_SHIFT; -+ reg_width[i] = PWM_PERIOD_CYCLES_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset[i]); -+ temp = SET_BITS(reg_shift[i], reg_width[i], temp, (entire_cycles - 1)); -+ sunxi_pwm_writel(chip, reg_offset[i], temp); -+ } -+ -+ pwm_debug("active_cycles=%lu entire_cycles=%lu prescale=%u div_m=%u\n", -+ active_cycles, entire_cycles, prescale, div_m); -+ -+ /* config dead zone, one config for two pwm */ -+ reg_offset[0] = reg_dz_en_offset[0]; -+ reg_shift[0] = PWM_PDZINTV_SHIFT; -+ reg_width[0] = PWM_PDZINTV_WIDTH; -+ temp = sunxi_pwm_readl(chip, reg_offset[0]); -+ temp = SET_BITS(reg_shift[0], reg_width[0], temp, (unsigned int)clk_temp); -+ sunxi_pwm_writel(chip, reg_offset[0], temp); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, -+ int duty_ns, int period_ns) -+{ -+ int bind_num; -+ -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ bind_num = pc->config[pwm->hwpwm].bind_pwm; -+ if (bind_num == 255) { -+ sunxi_pwm_config_single(chip, pwm, duty_ns, period_ns); -+ } -+ else { -+ sunxi_pwm_config_dual(chip, pwm, duty_ns, period_ns, bind_num); -+ } -+ -+ return 0; -+} -+ -+static int sunxi_pwm_enable_single(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ unsigned int value = 0, index = 0; -+ unsigned int reg_offset, reg_shift; -+ struct device_node *sub_np; -+ struct platform_device *pwm_pdevice; -+ int ret; -+ -+ index = pwm->hwpwm; -+ sub_np = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", index); -+ if (IS_ERR_OR_NULL(sub_np)) -+ { -+ pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__); -+ return -ENODEV; -+ } -+ pwm_pdevice = of_find_device_by_node(sub_np); -+ if (IS_ERR_OR_NULL(pwm_pdevice)) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return -ENODEV; -+ } -+ ret = sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_ACTIVE); -+ if (ret != 0) -+ return ret; -+ -+ /* enable clk for pwm controller */ -+ get_pccr_reg_offset(index, ®_offset); -+ reg_shift = PWM_CLK_GATING_SHIFT; -+ value = sunxi_pwm_readl(chip, reg_offset); -+ value = SET_BITS(reg_shift, 1, value, 1); -+ sunxi_pwm_writel(chip, reg_offset, value); -+ -+ /* enable pwm controller */ -+ reg_offset = PWM_PER; -+ reg_shift = index; -+ value = sunxi_pwm_readl(chip, reg_offset); -+ value = SET_BITS(reg_shift, 1, value, 1); -+ sunxi_pwm_writel(chip, reg_offset, value); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_enable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num) -+{ -+ u32 value[2] = {0}; -+ unsigned int reg_offset[2], reg_shift[2], reg_width[2]; -+ struct device_node *sub_np[2]; -+ struct platform_device *pwm_pdevice[2]; -+ int i = 0, ret = 0; -+ unsigned int pwm_index[2] = {0}; -+ -+ pwm_index[0] = pwm->hwpwm; -+ pwm_index[1] = bind_num - (int)chip->id; -+ -+ /*set current pwm pin state*/ -+ sub_np[0] = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", pwm_index[0]); -+ if (IS_ERR_OR_NULL(sub_np[0])) -+ { -+ pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__); -+ return -ENODEV; -+ } -+ pwm_pdevice[0] = of_find_device_by_node(sub_np[0]); -+ if (IS_ERR_OR_NULL(pwm_pdevice[0])) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return -ENODEV; -+ } -+ -+ /*set bind pwm pin state*/ -+ sub_np[1] = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", pwm_index[1]); -+ if (IS_ERR_OR_NULL(sub_np[1])) -+ { -+ pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__); -+ return -ENODEV; -+ } -+ pwm_pdevice[1] = of_find_device_by_node(sub_np[1]); -+ if (IS_ERR_OR_NULL(pwm_pdevice[1])) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return -ENODEV; -+ } -+ -+ ret = sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_ACTIVE); -+ if (ret != 0) -+ return ret; -+ ret = sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_ACTIVE); -+ if (ret != 0) -+ return ret; -+ -+ /* enable clk for pwm controller */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ get_pccr_reg_offset(pwm_index[i], ®_offset[i]); -+ reg_shift[i] = PWM_CLK_GATING_SHIFT; -+ reg_width[i] = PWM_CLK_GATING_WIDTH; -+ value[i] = sunxi_pwm_readl(chip, reg_offset[i]); -+ value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1); -+ sunxi_pwm_writel(chip, reg_offset[i], value[i]); -+ } -+ -+ /* enable pwm controller */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_offset[i] = PWM_PER; -+ reg_shift[i] = pwm_index[i]; -+ reg_width[i] = 0x1; -+ value[i] = sunxi_pwm_readl(chip, reg_offset[i]); -+ value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1); -+ sunxi_pwm_writel(chip, reg_offset[i], value[i]); -+ } -+ -+ return 0; -+} -+ -+static int sunxi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ int bind_num; -+ int ret = 0; -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ bind_num = pc->config[pwm->hwpwm].bind_pwm; -+ if (bind_num == 255) -+ ret = sunxi_pwm_enable_single(chip, pwm); -+ else -+ ret = sunxi_pwm_enable_dual(chip, pwm, bind_num); -+ -+ sunxi_pwm_set_polarity(chip, pwm, PWM_POLARITY_NORMAL); -+ -+ return ret; -+} -+ -+static void sunxi_pwm_disable_single(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ u32 value = 0, index = 0; -+ unsigned int reg_offset, reg_shift, reg_width; -+ struct device_node *sub_np; -+ struct platform_device *pwm_pdevice; -+ -+ index = pwm->hwpwm; -+ sub_np = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", index); -+ if (IS_ERR_OR_NULL(sub_np)) -+ { -+ pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__); -+ return; -+ } -+ pwm_pdevice = of_find_device_by_node(sub_np); -+ if (IS_ERR_OR_NULL(pwm_pdevice)) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return; -+ } -+ -+ /* disable pwm controller */ -+ reg_offset = PWM_PER; -+ reg_shift = index; -+ reg_width = 0x1; -+ value = sunxi_pwm_readl(chip, reg_offset); -+ value = SET_BITS(reg_shift, reg_width, value, 0); -+ sunxi_pwm_writel(chip, reg_offset, value); -+ -+ /* -+ * 0 , 1 --> 0 -+ * 2 , 3 --> 2 -+ * 4 , 5 --> 4 -+ * 6 , 7 --> 6 -+ */ -+ reg_shift &= ~(1); -+ -+ if (GET_BITS(reg_shift, 2, value) == 0) -+ { -+ /* disable clk for pwm controller. */ -+ get_pccr_reg_offset(index, ®_offset); -+ reg_shift = PWM_CLK_GATING_SHIFT; -+ reg_width = 0x1; -+ value = sunxi_pwm_readl(chip, reg_offset); -+ value = SET_BITS(reg_shift, reg_width, value, 0); -+ sunxi_pwm_writel(chip, reg_offset, value); -+ } -+ -+ // sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_SLEEP); -+} -+ -+static void sunxi_pwm_disable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num) -+{ -+ u32 value[2] = {0}; -+ unsigned int reg_offset[2], reg_shift[2], reg_width[2]; -+ struct device_node *sub_np[2]; -+ struct platform_device *pwm_pdevice[2]; -+ int i = 0; -+ unsigned int pwm_index[2] = {0}; -+ -+ pwm_index[0] = pwm->hwpwm; -+ pwm_index[1] = bind_num - (int)chip->id; -+ -+ /* get current index pwm device */ -+ sub_np[0] = of_parse_phandle(pwmchip_parent(chip)->of_node, "pwms", pwm_index[0]); -+ if (IS_ERR_OR_NULL(sub_np[0])) -+ { -+ pr_err("%s: can't parse \"pwms\" property\n", __func__); -+ return; -+ } -+ pwm_pdevice[0] = of_find_device_by_node(sub_np[0]); -+ if (IS_ERR_OR_NULL(pwm_pdevice[0])) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return; -+ } -+ /* get bind pwm device */ -+ sub_np[1] = of_parse_phandle(pwmchip_parent(chip)->of_node, "pwms", pwm_index[1]); -+ if (IS_ERR_OR_NULL(sub_np[1])) -+ { -+ pr_err("%s: can't parse \"pwms\" property\n", __func__); -+ return; -+ } -+ pwm_pdevice[1] = of_find_device_by_node(sub_np[1]); -+ if (IS_ERR_OR_NULL(pwm_pdevice[1])) -+ { -+ pr_err("%s: can't parse pwm device\n", __func__); -+ return; -+ } -+ -+ /* disable pwm controller */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ reg_offset[i] = PWM_PER; -+ reg_shift[i] = pwm_index[i]; -+ reg_width[i] = 0x1; -+ value[i] = sunxi_pwm_readl(chip, reg_offset[i]); -+ value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0); -+ sunxi_pwm_writel(chip, reg_offset[i], value[i]); -+ } -+ -+ /* disable pwm clk gating */ -+ for (i = 0; i < PWM_BIND_NUM; i++) -+ { -+ get_pccr_reg_offset(pwm_index[i], ®_offset[i]); -+ reg_shift[i] = PWM_CLK_GATING_SHIFT; -+ reg_width[i] = 0x1; -+ value[i] = sunxi_pwm_readl(chip, reg_offset[i]); -+ value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0); -+ sunxi_pwm_writel(chip, reg_offset[i], value[i]); -+ } -+ -+ /* disable pwm dead zone,one for the two pwm */ -+ get_pdzcr_reg_offset(pwm_index[0], ®_offset[0]); -+ reg_shift[0] = PWM_DZ_EN_SHIFT; -+ reg_width[0] = PWM_DZ_EN_WIDTH; -+ value[0] = sunxi_pwm_readl(chip, reg_offset[0]); -+ value[0] = SET_BITS(reg_shift[0], reg_width[0], value[0], 0); -+ sunxi_pwm_writel(chip, reg_offset[0], value[0]); -+ -+ /* config pin sleep */ -+ // sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_SLEEP); -+ // sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_SLEEP); -+} -+ -+static void sunxi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ int bind_num; -+ struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip); -+ -+ bind_num = pc->config[pwm->hwpwm].bind_pwm; -+ if (bind_num == 255) -+ sunxi_pwm_disable_single(chip, pwm); -+ else -+ sunxi_pwm_disable_dual(chip, pwm, bind_num); -+} -+ -+static int sunxi_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, -+ const struct pwm_state *state) -+{ -+ int err; -+ bool enabled = pwm->state.enabled; -+ -+ if (state->polarity != pwm->state.polarity) { -+ /* -+ * Changing the polarity of a running PWM is only allowed when -+ * the PWM driver implements ->apply(). -+ */ -+ if (enabled) { -+ sunxi_pwm_disable(chip, pwm); -+ -+ enabled = false; -+ } -+ -+ err = sunxi_pwm_set_polarity(chip, pwm, state->polarity); -+ if (err) -+ return err; -+ } -+ -+ -+ if (!state->enabled) { -+ if (enabled) { -+ sunxi_pwm_disable(chip, pwm); -+ } -+ -+ return 0; -+ } -+ -+ /* -+ * We cannot skip calling ->config even if state->period == -+ * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle -+ * because we might have exited early in the last call to -+ * pwm_apply_state because of !state->enabled and so the two values in -+ * pwm->state might not be configured in hardware. -+ */ -+ err = sunxi_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); -+ if (err) { -+ return err; -+ } -+ -+ if (!enabled) -+ err = sunxi_pwm_enable(chip, pwm); -+ -+ return err; -+} -+ -+static const struct pwm_ops sunxi_pwm_ops = { -+ .apply = sunxi_pwm_apply, -+}; -+ -+static int sunxi_pwm_probe(struct platform_device *pdev) -+{ -+ int ret; -+ struct sunxi_pwm_chip *pwm; -+ struct device_node *np = pdev->dev.of_node; -+ int i; -+ struct platform_device *pwm_pdevice; -+ struct device_node *sub_np; -+ -+ pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); -+ if (!pwm) -+ { -+ dev_err(&pdev->dev, "failed to allocate memory!\n"); -+ return -ENOMEM; -+ } -+ -+ /* io map pwm base */ -+ pwm->base = (void __iomem *)of_iomap(pdev->dev.of_node, 0); -+ if (!pwm->base) -+ { -+ dev_err(&pdev->dev, "unable to map pwm registers\n"); -+ ret = -EINVAL; -+ goto err_iomap; -+ } -+ -+ /* read property pwm-number */ -+ ret = of_property_read_u32(np, "pwm-number", &pwm->chip.npwm); -+ if (ret < 0) -+ { -+ dev_err(&pdev->dev, "failed to get pwm number: %d, force to one!\n", ret); -+ /* force to one pwm if read property fail */ -+ pwm->chip.npwm = 1; -+ } -+ -+ /* read property pwm-base */ -+ ret = of_property_read_u32(np, "pwm-base", &pwm->chip.id); -+ if (ret < 0) -+ { -+ dev_err(&pdev->dev, "failed to get pwm-base: %d, force to -1 !\n", ret); -+ /* force to one pwm if read property fail */ -+ pwm->chip.id = -1; -+ } -+ pwm->chip.dev.parent = &pdev->dev; -+ pwm->chip.ops = &sunxi_pwm_ops; -+ -+ /* add pwm chip to pwm-core */ -+ ret = pwmchip_add(&pwm->chip); -+ if (ret < 0) -+ { -+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); -+ goto err_add; -+ } -+ platform_set_drvdata(pdev, pwm); -+ -+ pwm->config = devm_kzalloc(&pdev->dev, sizeof(*pwm->config) * pwm->chip.npwm, GFP_KERNEL); -+ if (!pwm->config) -+ { -+ dev_err(&pdev->dev, "failed to allocate memory!\n"); -+ goto err_alloc; -+ } -+ -+ for (i = 0; i < pwm->chip.npwm; i++) -+ { -+ sub_np = of_parse_phandle(np, "sunxi-pwms", i); -+ if (IS_ERR_OR_NULL(sub_np)) -+ { -+ pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__); -+ return -EINVAL; -+ } -+ -+ pwm_pdevice = of_find_device_by_node(sub_np); -+ ret = sunxi_pwm_get_config(pwm_pdevice, &pwm->config[i]); -+ if (ret) -+ { -+ pr_err("Get config failed,exit!\n"); -+ goto err_get_config; -+ } -+ } -+ -+ pwm->clk = devm_clk_get_optional(&pdev->dev, "mod"); -+ if (IS_ERR(pwm->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(pwm->clk), -+ "get mod clock failed\n"); -+ -+ pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); -+ if (IS_ERR(pwm->bus_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(pwm->bus_clk), -+ "get bus clock failed\n"); -+ -+ pwm->pwm_rst_clk = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ if (IS_ERR(pwm->pwm_rst_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(pwm->pwm_rst_clk), -+ "get reset failed\n"); -+ -+ /* Deassert reset */ -+ ret = reset_control_deassert(pwm->pwm_rst_clk); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "cannot deassert reset control: %pe\n", -+ ERR_PTR(ret)); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(pwm->clk); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "cannot prepare and enable clk %pe\n", -+ ERR_PTR(ret)); -+ goto err_alloc; -+ } -+ -+ ret = clk_prepare_enable(pwm->bus_clk); -+ if (ret) -+ { -+ dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n", -+ ERR_PTR(ret)); -+ goto err_alloc; -+ } -+ -+ return 0; -+ -+err_get_config: -+err_alloc: -+ pwmchip_remove(&pwm->chip); -+err_add: -+ iounmap(pwm->base); -+err_iomap: -+ return ret; -+} -+ -+static int sunxi_pwm_remove(struct platform_device *pdev) -+{ -+ struct sunxi_pwm_chip *pwm = platform_get_drvdata(pdev); -+ clk_disable(pwm->clk); -+ clk_disable(pwm->bus_clk); -+ reset_control_assert(pwm->pwm_rst_clk); -+ pwmchip_remove(&pwm->chip); -+ -+ return 0; -+} -+ -+static int sunxi_pwm_suspend(struct platform_device *pdev, pm_message_t state) -+{ -+ return 0; -+} -+ -+static int sunxi_pwm_resume(struct platform_device *pdev) -+{ -+ return 0; -+} -+ -+#if !IS_ENABLED(CONFIG_OF) -+struct platform_device sunxi_pwm_device = { -+ .name = "sunxi_pwm", -+ .id = -1, -+}; -+#else -+static const struct of_device_id sunxi_pwm_match[] = { -+ { -+ .compatible = "allwinner,sun50i-h616-pwm", -+ }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, sunxi_pwm_match); -+#endif -+ -+static struct platform_driver sunxi_pwm_driver = { -+ .probe = sunxi_pwm_probe, -+ .remove = sunxi_pwm_remove, -+ .suspend = sunxi_pwm_suspend, -+ .resume = sunxi_pwm_resume, -+ .driver = { -+ .name = "sunxi_pwm", -+ .owner = THIS_MODULE, -+ .of_match_table = sunxi_pwm_match, -+ }, -+}; -+ -+static int __init pwm_module_init(void) -+{ -+ int ret = 0; -+ -+#if !IS_ENABLED(CONFIG_OF) -+ ret = platform_device_register(&sunxi_pwm_device); -+#endif -+ if (ret == 0) -+ { -+ ret = platform_driver_register(&sunxi_pwm_driver); -+ } -+ -+ return ret; -+} -+ -+static void __exit pwm_module_exit(void) -+{ -+ platform_driver_unregister(&sunxi_pwm_driver); -+#if !IS_ENABLED(CONFIG_OF) -+ platform_device_unregister(&sunxi_pwm_device); -+#endif -+} -+ -+subsys_initcall(pwm_module_init); -+module_exit(pwm_module_exit); -+ -+MODULE_AUTHOR("zengqi"); -+MODULE_AUTHOR("liuli"); -+MODULE_DESCRIPTION("pwm driver"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:sunxi-pwm"); -diff --git a/drivers/pwm/pwm-sunxi-enhance.h b/drivers/pwm/pwm-sunxi-enhance.h -new file mode 100644 -index 000000000000..e25e10bf5a3d ---- /dev/null -+++ b/drivers/pwm/pwm-sunxi-enhance.h -@@ -0,0 +1,60 @@ -+/* -+ * drivers/pwm/pwm-sunxi-new.h -+ * -+ * Allwinnertech pulse-width-modulation controller driver -+ * -+ * Copyright (C) 2018 AllWinner -+ * -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+#ifndef __PWM_SUNXI_NEW__H__ -+#define __PWM_SUNXI_NEW__H__ -+ -+#define PWM_PIER (0x0000) -+#define PWM_PISR (0x0004) -+#define PWM_CIER (0x0010) -+#define PWM_CISR (0x0014) -+#define PWM_PCCR01 (0x0020) -+#define PWM_PCCR23 (0x0024) -+#define PWM_PCCR45 (0x0028) -+#define PWM_PCCR67 (0x002c) -+#define PWM_PDZCR01 (0x0030) -+#define PWM_PDZCR23 (0x0034) -+#define PWM_PDZCR45 (0x0038) -+#define PWM_PDZCR67 (0x003c) -+#define PWM_PER (0x0040) -+#define PWM_CER (0x0044) -+ -+#define PWM_PCR_BASE (0x0060 + 0x0000) -+#define PWM_PPR_BASE (0x0060 + 0x0004) -+#define PWM_PCNTR_BASE (0x0060 + 0x0008) -+#define PWM_CCR_BASE (0x0060 + 0x000c) -+#define PWM_CRLR_BASE (0x0060 + 0x0010) -+#define PWM_CFLR_BASE (0x0060 + 0x0014) -+#define PWM_PCCR8 (0x0300) -+ -+#define PWM_ACT_STA_SHIFT 0x8 -+#define PWM_ACT_STA_WIDTH 0x1 -+#define PWM_CLK_SRC_SHIFT 0x7 -+#define PWM_CLK_SRC_WIDTH 0x2 -+#define PWM_DIV_M_SHIFT 0x0 -+#define PWM_DIV_M_WIDTH 0x4 -+#define PWM_PRESCAL_SHIFT 0x0 -+#define PWM_PRESCAL_WIDTH 0x8 -+#define PWM_ACT_CYCLES_SHIFT 0x0 -+#define PWM_ACT_CYCLES_WIDTH 0x10 -+#define PWM_PERIOD_CYCLES_SHIFT 0x10 -+#define PWM_PERIOD_CYCLES_WIDTH 0x10 -+#define PWM_DZ_EN_SHIFT 0x0 -+#define PWM_DZ_EN_WIDTH 0x1 -+#define PWM_PDZINTV_SHIFT 0x8 -+#define PWM_PDZINTV_WIDTH 0x8 -+#define PWM_BYPASS_WIDTH 0x1 -+#define PWM_CLK_GATING_SHIFT 0x4 -+#define PWM_CLK_GATING_WIDTH 0x1 -+ -+#endif -+ --- -GitLab diff --git a/patch/kernel/archive/sunxi-6.10/series.armbian b/patch/kernel/archive/sunxi-6.10/series.armbian index 58afe25c15dc..2627d5a08439 100644 --- a/patch/kernel/archive/sunxi-6.10/series.armbian +++ b/patch/kernel/archive/sunxi-6.10/series.armbian @@ -170,3 +170,5 @@ patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch + patches.armbian/drv-thermal-sun8i_thermal-Add-for-H616.patch + patches.armbian/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch diff --git a/patch/kernel/archive/sunxi-6.10/series.conf b/patch/kernel/archive/sunxi-6.10/series.conf index 060eb114452c..29504090cc86 100644 --- a/patch/kernel/archive/sunxi-6.10/series.conf +++ b/patch/kernel/archive/sunxi-6.10/series.conf @@ -434,3 +434,5 @@ patches.armbian/Sound-for-H616-H618-Allwinner-SOCs.patch patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-HDMI.patch patches.armbian/ARM64-dts-sun50i-h616-BigTreeTech-CB1-Enable-EMAC1.patch + patches.armbian/drv-thermal-sun8i_thermal-Add-for-H616.patch + patches.armbian/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch diff --git a/patch/kernel/archive/sunxi-6.10/series.megous b/patch/kernel/archive/sunxi-6.10/series.megous index b23d8216f57b..867f2197c9d6 100644 --- a/patch/kernel/archive/sunxi-6.10/series.megous +++ b/patch/kernel/archive/sunxi-6.10/series.megous @@ -260,13 +260,3 @@ - patches.megous/Add-README.md-with-information-and-u-boot-patches.patch patches.megous/Defconfigs-for-all-my-devices.patch patches.megous/Update-defconfigs.patch - patches.megous/drv-nvmem-sunxi_sid-Support-SID-on-H616.patch - patches.megous/drv-thermal-sun8i_thermal-Add-for-H616.patch - patches.megous/nvmem-sunxi_sid-add-sunxi_get_soc_chipid-sunxi_get_serial.mk_format_patch - patches.megous/drv-iio-adc-axp20x_adc-arm64-dts-axp803-hwmon-enable-thermal.patch - patches.megous/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch - patches.megous/driver-allwinner-h618-emac.patch - patches.megous/arm64-dts-allwinner-h616-Add-thermal-sensor-and-thermal-zones.patch - patches.megous/arm64-dts-allwinner-sun50i-h616-Add-GPU-node.patch - patches.megous/arm64-dts-allwinner-h616-Add-efuse_xlate-cpu-frequency-scaling-.patch - patches.megous/arm64-dts-sun50i-h618-orangepi-zero2w-add-dtb.patch