114c32614STim Schendekehl /*
214c32614STim Schendekehl * (C) Copyright 2011
314c32614STim Schendekehl * egnite GmbH <info@egnite.de>
414c32614STim Schendekehl *
5*1a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
614c32614STim Schendekehl */
714c32614STim Schendekehl
814c32614STim Schendekehl /*
914c32614STim Schendekehl * Ethernut 5 power management support
1014c32614STim Schendekehl *
1114c32614STim Schendekehl * This board may be supplied via USB, IEEE 802.3af PoE or an
1214c32614STim Schendekehl * auxiliary DC input. An on-board ATmega168 microcontroller,
1314c32614STim Schendekehl * the so called power management controller or PMC, is used
1414c32614STim Schendekehl * to select the supply source and to switch on and off certain
1514c32614STim Schendekehl * energy consuming board components. This allows to reduce the
1614c32614STim Schendekehl * total stand-by consumption to less than 70mW.
1714c32614STim Schendekehl *
1814c32614STim Schendekehl * The main CPU communicates with the PMC via I2C. When
1914c32614STim Schendekehl * CONFIG_CMD_BSP is defined in the board configuration file,
2014c32614STim Schendekehl * then the board specific command 'pwrman' becomes available,
2114c32614STim Schendekehl * which allows to manually deal with the PMC.
2214c32614STim Schendekehl *
2314c32614STim Schendekehl * Two distinct registers are provided by the PMC for enabling
2414c32614STim Schendekehl * and disabling specific features. This avoids the often seen
2514c32614STim Schendekehl * read-modify-write cycle or shadow register requirement.
2614c32614STim Schendekehl * Additional registers are available to query the board
2714c32614STim Schendekehl * status and temperature, the auxiliary voltage and to control
2814c32614STim Schendekehl * the green user LED that is integrated in the reset switch.
2914c32614STim Schendekehl *
3014c32614STim Schendekehl * Note, that the AVR firmware of the PMC is released under BSDL.
3114c32614STim Schendekehl *
3214c32614STim Schendekehl * For additional information visit the project home page at
3314c32614STim Schendekehl * http://www.ethernut.de/
3414c32614STim Schendekehl */
3514c32614STim Schendekehl #include <common.h>
3614c32614STim Schendekehl #include <asm/arch/at91sam9260.h>
3714c32614STim Schendekehl #include <asm/arch/at91_common.h>
3814c32614STim Schendekehl #include <asm/arch/gpio.h>
3914c32614STim Schendekehl #include <asm/io.h>
4014c32614STim Schendekehl #include <i2c.h>
4114c32614STim Schendekehl
4214c32614STim Schendekehl #include "ethernut5_pwrman.h"
4314c32614STim Schendekehl
4414c32614STim Schendekehl /* PMC firmware version */
4514c32614STim Schendekehl static int pwrman_major;
4614c32614STim Schendekehl static int pwrman_minor;
4714c32614STim Schendekehl
4814c32614STim Schendekehl /*
4914c32614STim Schendekehl * Enable Ethernut 5 power management.
5014c32614STim Schendekehl *
5114c32614STim Schendekehl * This function must be called during board initialization.
5214c32614STim Schendekehl * While we are using u-boot's I2C subsystem, it may be required
5314c32614STim Schendekehl * to enable the serial port before calling this function,
5414c32614STim Schendekehl * in particular when debugging is enabled.
5514c32614STim Schendekehl *
5614c32614STim Schendekehl * If board specific commands are not available, we will activate
5714c32614STim Schendekehl * all board components.
5814c32614STim Schendekehl */
ethernut5_power_init(void)5914c32614STim Schendekehl void ethernut5_power_init(void)
6014c32614STim Schendekehl {
6114c32614STim Schendekehl pwrman_minor = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VERS);
6214c32614STim Schendekehl pwrman_major = pwrman_minor >> 4;
6314c32614STim Schendekehl pwrman_minor &= 15;
6414c32614STim Schendekehl
6514c32614STim Schendekehl #ifndef CONFIG_CMD_BSP
6614c32614STim Schendekehl /* Do not modify anything, if we do not have a known version. */
6714c32614STim Schendekehl if (pwrman_major == 2) {
6814c32614STim Schendekehl /* Without board specific commands we enable all features. */
6914c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, ~PWRMAN_ETHRST);
7014c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
7114c32614STim Schendekehl }
7214c32614STim Schendekehl #endif
7314c32614STim Schendekehl }
7414c32614STim Schendekehl
7514c32614STim Schendekehl /*
7614c32614STim Schendekehl * Reset Ethernet PHY.
7714c32614STim Schendekehl *
7814c32614STim Schendekehl * This function allows the re-configure the PHY after
7914c32614STim Schendekehl * changing its strap pins.
8014c32614STim Schendekehl */
ethernut5_phy_reset(void)8114c32614STim Schendekehl void ethernut5_phy_reset(void)
8214c32614STim Schendekehl {
8314c32614STim Schendekehl /* Do not modify anything, if we do not have a known version. */
8414c32614STim Schendekehl if (pwrman_major != 2)
8514c32614STim Schendekehl return;
8614c32614STim Schendekehl
8714c32614STim Schendekehl /*
8814c32614STim Schendekehl * Make sure that the Ethernet clock is enabled and the PHY reset
8914c32614STim Schendekehl * is disabled for at least 100 us.
9014c32614STim Schendekehl */
9114c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHCLK);
9214c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
9314c32614STim Schendekehl udelay(100);
9414c32614STim Schendekehl
9514c32614STim Schendekehl /*
9614c32614STim Schendekehl * LAN8710 strap pins are
9714c32614STim Schendekehl * PA14 => PHY MODE0
9814c32614STim Schendekehl * PA15 => PHY MODE1
9914c32614STim Schendekehl * PA17 => PHY MODE2 => 111b all capable
10014c32614STim Schendekehl * PA18 => PHY ADDR0 => 0b
10114c32614STim Schendekehl */
10214c32614STim Schendekehl at91_set_pio_input(AT91_PIO_PORTA, 14, 1);
10314c32614STim Schendekehl at91_set_pio_input(AT91_PIO_PORTA, 15, 1);
10414c32614STim Schendekehl at91_set_pio_input(AT91_PIO_PORTA, 17, 1);
10514c32614STim Schendekehl at91_set_pio_input(AT91_PIO_PORTA, 18, 0);
10614c32614STim Schendekehl
10714c32614STim Schendekehl /* Activate PHY reset for 100 us. */
10814c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHRST);
10914c32614STim Schendekehl udelay(100);
11014c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
11114c32614STim Schendekehl
11214c32614STim Schendekehl at91_set_pio_input(AT91_PIO_PORTA, 14, 1);
11314c32614STim Schendekehl }
11414c32614STim Schendekehl
11514c32614STim Schendekehl /*
11614c32614STim Schendekehl * Output the firmware version we got during initialization.
11714c32614STim Schendekehl */
ethernut5_print_version(void)11814c32614STim Schendekehl void ethernut5_print_version(void)
11914c32614STim Schendekehl {
12014c32614STim Schendekehl printf("%u.%u\n", pwrman_major, pwrman_minor);
12114c32614STim Schendekehl }
12214c32614STim Schendekehl
12314c32614STim Schendekehl /*
12414c32614STim Schendekehl * All code below this point is optional and implements
12514c32614STim Schendekehl * the 'pwrman' command.
12614c32614STim Schendekehl */
12714c32614STim Schendekehl #ifdef CONFIG_CMD_BSP
12814c32614STim Schendekehl
12914c32614STim Schendekehl /* Human readable names of PMC features */
13014c32614STim Schendekehl char *pwrman_feat[8] = {
13114c32614STim Schendekehl "board", "vbin", "vbout", "mmc",
13214c32614STim Schendekehl "rs232", "ethclk", "ethrst", "wakeup"
13314c32614STim Schendekehl };
13414c32614STim Schendekehl
13514c32614STim Schendekehl /*
13614c32614STim Schendekehl * Print all feature names, that have its related flags enabled.
13714c32614STim Schendekehl */
print_flagged_features(u8 flags)13814c32614STim Schendekehl static void print_flagged_features(u8 flags)
13914c32614STim Schendekehl {
14014c32614STim Schendekehl int i;
14114c32614STim Schendekehl
14214c32614STim Schendekehl for (i = 0; i < 8; i++) {
14314c32614STim Schendekehl if (flags & (1 << i))
14414c32614STim Schendekehl printf("%s ", pwrman_feat[i]);
14514c32614STim Schendekehl }
14614c32614STim Schendekehl }
14714c32614STim Schendekehl
14814c32614STim Schendekehl /*
14914c32614STim Schendekehl * Return flags of a given list of feature names.
15014c32614STim Schendekehl *
15114c32614STim Schendekehl * The function stops at the first unknown list entry and
15214c32614STim Schendekehl * returns the number of detected names as a function result.
15314c32614STim Schendekehl */
feature_flags(char * const names[],int num,u8 * flags)15414c32614STim Schendekehl static int feature_flags(char * const names[], int num, u8 *flags)
15514c32614STim Schendekehl {
15614c32614STim Schendekehl int i, j;
15714c32614STim Schendekehl
15814c32614STim Schendekehl *flags = 0;
15914c32614STim Schendekehl for (i = 0; i < num; i++) {
16014c32614STim Schendekehl for (j = 0; j < 8; j++) {
16114c32614STim Schendekehl if (strcmp(pwrman_feat[j], names[i]) == 0) {
16214c32614STim Schendekehl *flags |= 1 << j;
16314c32614STim Schendekehl break;
16414c32614STim Schendekehl }
16514c32614STim Schendekehl }
16614c32614STim Schendekehl if (j > 7)
16714c32614STim Schendekehl break;
16814c32614STim Schendekehl }
16914c32614STim Schendekehl return i;
17014c32614STim Schendekehl }
17114c32614STim Schendekehl
ethernut5_print_power(void)17214c32614STim Schendekehl void ethernut5_print_power(void)
17314c32614STim Schendekehl {
17414c32614STim Schendekehl u8 flags;
17514c32614STim Schendekehl int i;
17614c32614STim Schendekehl
17714c32614STim Schendekehl flags = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA);
17814c32614STim Schendekehl for (i = 0; i < 2; i++) {
17914c32614STim Schendekehl if (flags) {
18014c32614STim Schendekehl print_flagged_features(flags);
18114c32614STim Schendekehl printf("%s\n", i ? "off" : "on");
18214c32614STim Schendekehl }
18314c32614STim Schendekehl flags = ~flags;
18414c32614STim Schendekehl }
18514c32614STim Schendekehl }
18614c32614STim Schendekehl
ethernut5_print_celsius(void)18714c32614STim Schendekehl void ethernut5_print_celsius(void)
18814c32614STim Schendekehl {
18914c32614STim Schendekehl int val;
19014c32614STim Schendekehl
19114c32614STim Schendekehl /* Read ADC value from LM50 and return Celsius degrees. */
19214c32614STim Schendekehl val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_TEMP);
19314c32614STim Schendekehl val *= 5000; /* 100mV/degree with 5V reference */
19414c32614STim Schendekehl val += 128; /* 8 bit resolution */
19514c32614STim Schendekehl val /= 256;
19614c32614STim Schendekehl val -= 450; /* Celsius offset, still x10 */
19714c32614STim Schendekehl /* Output full degrees. */
19814c32614STim Schendekehl printf("%d\n", (val + 5) / 10);
19914c32614STim Schendekehl }
20014c32614STim Schendekehl
ethernut5_print_voltage(void)20114c32614STim Schendekehl void ethernut5_print_voltage(void)
20214c32614STim Schendekehl {
20314c32614STim Schendekehl int val;
20414c32614STim Schendekehl
20514c32614STim Schendekehl /* Read ADC value from divider and return voltage. */
20614c32614STim Schendekehl val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VAUX);
20714c32614STim Schendekehl /* Resistors are 100k and 12.1k */
20814c32614STim Schendekehl val += 5;
20914c32614STim Schendekehl val *= 180948;
21014c32614STim Schendekehl val /= 100000;
21114c32614STim Schendekehl val++;
21214c32614STim Schendekehl /* Calculation was done in 0.1V units. */
21314c32614STim Schendekehl printf("%d\n", (val + 5) / 10);
21414c32614STim Schendekehl }
21514c32614STim Schendekehl
21614c32614STim Schendekehl /*
21714c32614STim Schendekehl * Process the board specific 'pwrman' command.
21814c32614STim Schendekehl */
do_pwrman(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])21914c32614STim Schendekehl int do_pwrman(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
22014c32614STim Schendekehl {
22114c32614STim Schendekehl u8 val;
22214c32614STim Schendekehl int i;
22314c32614STim Schendekehl
22414c32614STim Schendekehl if (argc == 1) {
22514c32614STim Schendekehl ethernut5_print_power();
22614c32614STim Schendekehl } else if (argc == 2 && strcmp(argv[1], "reset") == 0) {
22714c32614STim Schendekehl at91_set_pio_output(AT91_PIO_PORTB, 8, 1);
22814c32614STim Schendekehl udelay(100);
22914c32614STim Schendekehl at91_set_pio_output(AT91_PIO_PORTB, 8, 0);
23014c32614STim Schendekehl udelay(100000);
23114c32614STim Schendekehl } else if (argc == 2 && strcmp(argv[1], "temp") == 0) {
23214c32614STim Schendekehl ethernut5_print_celsius();
23314c32614STim Schendekehl } else if (argc == 2 && strcmp(argv[1], "vaux") == 0) {
23414c32614STim Schendekehl ethernut5_print_voltage();
23514c32614STim Schendekehl } else if (argc == 2 && strcmp(argv[1], "version") == 0) {
23614c32614STim Schendekehl ethernut5_print_version();
23714c32614STim Schendekehl } else if (strcmp(argv[1], "led") == 0) {
23814c32614STim Schendekehl /* Control the green status LED. Blink frequency unit
23914c32614STim Schendekehl ** is 0.1s, very roughly. */
24014c32614STim Schendekehl if (argc == 2) {
24114c32614STim Schendekehl /* No more arguments, output current settings. */
24214c32614STim Schendekehl val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL);
24314c32614STim Schendekehl printf("led %u %u\n", val >> 4, val & 15);
24414c32614STim Schendekehl } else {
24514c32614STim Schendekehl /* First argument specifies the on-time. */
24614c32614STim Schendekehl val = (u8) simple_strtoul(argv[2], NULL, 0);
24714c32614STim Schendekehl val <<= 4;
24814c32614STim Schendekehl if (argc > 3) {
24914c32614STim Schendekehl /* Second argument specifies the off-time. */
25014c32614STim Schendekehl val |= (u8) (simple_strtoul(argv[3], NULL, 0)
25114c32614STim Schendekehl & 15);
25214c32614STim Schendekehl }
25314c32614STim Schendekehl /* Update the LED control register. */
25414c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL, val);
25514c32614STim Schendekehl }
25614c32614STim Schendekehl } else {
25714c32614STim Schendekehl /* We expect a list of features followed an optional status. */
25814c32614STim Schendekehl argc--;
25914c32614STim Schendekehl i = feature_flags(&argv[1], argc, &val);
26014c32614STim Schendekehl if (argc == i) {
26114c32614STim Schendekehl /* We got a list only, print status. */
26214c32614STim Schendekehl val &= i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_STA);
26314c32614STim Schendekehl if (val) {
26414c32614STim Schendekehl if (i > 1)
26514c32614STim Schendekehl print_flagged_features(val);
26614c32614STim Schendekehl printf("active\n");
26714c32614STim Schendekehl } else {
26814c32614STim Schendekehl printf("inactive\n");
26914c32614STim Schendekehl }
27014c32614STim Schendekehl } else {
27114c32614STim Schendekehl /* More arguments. */
27214c32614STim Schendekehl if (i == 0) {
27314c32614STim Schendekehl /* No given feature, use despensibles. */
27414c32614STim Schendekehl val = PWRMAN_DISPENSIBLE;
27514c32614STim Schendekehl }
27614c32614STim Schendekehl if (strcmp(argv[i + 1], "on") == 0) {
27714c32614STim Schendekehl /* Enable features. */
27814c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA,
27914c32614STim Schendekehl val);
28014c32614STim Schendekehl } else if (strcmp(argv[i + 1], "off") == 0) {
28114c32614STim Schendekehl /* Disable features. */
28214c32614STim Schendekehl i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS,
28314c32614STim Schendekehl val);
28414c32614STim Schendekehl } else {
28514c32614STim Schendekehl printf("Bad parameter %s\n", argv[i + 1]);
28614c32614STim Schendekehl return 1;
28714c32614STim Schendekehl }
28814c32614STim Schendekehl }
28914c32614STim Schendekehl }
29014c32614STim Schendekehl return 0;
29114c32614STim Schendekehl }
29214c32614STim Schendekehl
29314c32614STim Schendekehl U_BOOT_CMD(
29414c32614STim Schendekehl pwrman, CONFIG_SYS_MAXARGS, 1, do_pwrman,
29514c32614STim Schendekehl "power management",
29614c32614STim Schendekehl "- print settings\n"
29714c32614STim Schendekehl "pwrman feature ...\n"
29814c32614STim Schendekehl " - print status\n"
29914c32614STim Schendekehl "pwrman [feature ...] on|off\n"
30014c32614STim Schendekehl " - enable/disable specified or all dispensible features\n"
30114c32614STim Schendekehl "pwrman led [on-time [off-time]]\n"
30214c32614STim Schendekehl " - print or set led blink timer\n"
30314c32614STim Schendekehl "pwrman temp\n"
30414c32614STim Schendekehl " - print board temperature (Celsius)\n"
30514c32614STim Schendekehl "pwrman vaux\n"
30614c32614STim Schendekehl " - print auxiliary input voltage\n"
30714c32614STim Schendekehl "pwrman reset\n"
30814c32614STim Schendekehl " - reset power management controller\n"
30914c32614STim Schendekehl "pwrman version\n"
31014c32614STim Schendekehl " - print firmware version\n"
31114c32614STim Schendekehl "\n"
31214c32614STim Schendekehl " features, (*)=dispensible:\n"
31314c32614STim Schendekehl " board - 1.8V and 3.3V supply\n"
31414c32614STim Schendekehl " vbin - supply via USB device connector\n"
31514c32614STim Schendekehl " vbout - USB host connector supply(*)\n"
31614c32614STim Schendekehl " mmc - MMC slot supply(*)\n"
31714c32614STim Schendekehl " rs232 - RS232 driver\n"
31814c32614STim Schendekehl " ethclk - Ethernet PHY clock(*)\n"
31914c32614STim Schendekehl " ethrst - Ethernet PHY reset\n"
32014c32614STim Schendekehl " wakeup - RTC alarm"
32114c32614STim Schendekehl );
32214c32614STim Schendekehl #endif /* CONFIG_CMD_BSP */
323