xref: /openbmc/u-boot/board/egnite/ethernut5/ethernut5_pwrman.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
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