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