xref: /OK3568_Linux_fs/u-boot/board/egnite/ethernut5/ethernut5_pwrman.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * (C) Copyright 2011
3*4882a593Smuzhiyun  * egnite GmbH <info@egnite.de>
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * Ethernut 5 power management support
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * This board may be supplied via USB, IEEE 802.3af PoE or an
12*4882a593Smuzhiyun  * auxiliary DC input. An on-board ATmega168 microcontroller,
13*4882a593Smuzhiyun  * the so called power management controller or PMC, is used
14*4882a593Smuzhiyun  * to select the supply source and to switch on and off certain
15*4882a593Smuzhiyun  * energy consuming board components. This allows to reduce the
16*4882a593Smuzhiyun  * total stand-by consumption to less than 70mW.
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * The main CPU communicates with the PMC via I2C. When
19*4882a593Smuzhiyun  * CONFIG_CMD_BSP is defined in the board configuration file,
20*4882a593Smuzhiyun  * then the board specific command 'pwrman' becomes available,
21*4882a593Smuzhiyun  * which allows to manually deal with the PMC.
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  * Two distinct registers are provided by the PMC for enabling
24*4882a593Smuzhiyun  * and disabling specific features. This avoids the often seen
25*4882a593Smuzhiyun  * read-modify-write cycle or shadow register requirement.
26*4882a593Smuzhiyun  * Additional registers are available to query the board
27*4882a593Smuzhiyun  * status and temperature, the auxiliary voltage and to control
28*4882a593Smuzhiyun  * the green user LED that is integrated in the reset switch.
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * Note, that the AVR firmware of the PMC is released under BSDL.
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  * For additional information visit the project home page at
33*4882a593Smuzhiyun  * http://www.ethernut.de/
34*4882a593Smuzhiyun  */
35*4882a593Smuzhiyun #include <common.h>
36*4882a593Smuzhiyun #include <asm/arch/at91sam9260.h>
37*4882a593Smuzhiyun #include <asm/arch/at91_common.h>
38*4882a593Smuzhiyun #include <asm/arch/gpio.h>
39*4882a593Smuzhiyun #include <asm/io.h>
40*4882a593Smuzhiyun #include <i2c.h>
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #include "ethernut5_pwrman.h"
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* PMC firmware version */
45*4882a593Smuzhiyun static int pwrman_major;
46*4882a593Smuzhiyun static int pwrman_minor;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun  * Enable Ethernut 5 power management.
50*4882a593Smuzhiyun  *
51*4882a593Smuzhiyun  * This function must be called during board initialization.
52*4882a593Smuzhiyun  * While we are using u-boot's I2C subsystem, it may be required
53*4882a593Smuzhiyun  * to enable the serial port before calling this function,
54*4882a593Smuzhiyun  * in particular when debugging is enabled.
55*4882a593Smuzhiyun  *
56*4882a593Smuzhiyun  * If board specific commands are not available, we will activate
57*4882a593Smuzhiyun  * all board components.
58*4882a593Smuzhiyun  */
ethernut5_power_init(void)59*4882a593Smuzhiyun void ethernut5_power_init(void)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	pwrman_minor = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VERS);
62*4882a593Smuzhiyun 	pwrman_major = pwrman_minor >> 4;
63*4882a593Smuzhiyun 	pwrman_minor &= 15;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun #ifndef CONFIG_CMD_BSP
66*4882a593Smuzhiyun 	/* Do not modify anything, if we do not have a known version. */
67*4882a593Smuzhiyun 	if (pwrman_major == 2) {
68*4882a593Smuzhiyun 		/* Without board specific commands we enable all features. */
69*4882a593Smuzhiyun 		i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, ~PWRMAN_ETHRST);
70*4882a593Smuzhiyun 		i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun #endif
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun  * Reset Ethernet PHY.
77*4882a593Smuzhiyun  *
78*4882a593Smuzhiyun  * This function allows the re-configure the PHY after
79*4882a593Smuzhiyun  * changing its strap pins.
80*4882a593Smuzhiyun  */
ethernut5_phy_reset(void)81*4882a593Smuzhiyun void ethernut5_phy_reset(void)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	/* Do not modify anything, if we do not have a known version. */
84*4882a593Smuzhiyun 	if (pwrman_major != 2)
85*4882a593Smuzhiyun 		return;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	/*
88*4882a593Smuzhiyun 	 * Make sure that the Ethernet clock is enabled and the PHY reset
89*4882a593Smuzhiyun 	 * is disabled for at least 100 us.
90*4882a593Smuzhiyun 	 */
91*4882a593Smuzhiyun 	i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHCLK);
92*4882a593Smuzhiyun 	i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
93*4882a593Smuzhiyun 	udelay(100);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	/*
96*4882a593Smuzhiyun 	 * LAN8710 strap pins are
97*4882a593Smuzhiyun 	 * PA14 => PHY MODE0
98*4882a593Smuzhiyun 	 * PA15 => PHY MODE1
99*4882a593Smuzhiyun 	 * PA17 => PHY MODE2 => 111b all capable
100*4882a593Smuzhiyun 	 * PA18 => PHY ADDR0 => 0b
101*4882a593Smuzhiyun 	 */
102*4882a593Smuzhiyun 	at91_set_pio_input(AT91_PIO_PORTA, 14, 1);
103*4882a593Smuzhiyun 	at91_set_pio_input(AT91_PIO_PORTA, 15, 1);
104*4882a593Smuzhiyun 	at91_set_pio_input(AT91_PIO_PORTA, 17, 1);
105*4882a593Smuzhiyun 	at91_set_pio_input(AT91_PIO_PORTA, 18, 0);
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	/* Activate PHY reset for 100 us. */
108*4882a593Smuzhiyun 	i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA, PWRMAN_ETHRST);
109*4882a593Smuzhiyun 	udelay(100);
110*4882a593Smuzhiyun 	i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS, PWRMAN_ETHRST);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	at91_set_pio_input(AT91_PIO_PORTA, 14, 1);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /*
116*4882a593Smuzhiyun  * Output the firmware version we got during initialization.
117*4882a593Smuzhiyun  */
ethernut5_print_version(void)118*4882a593Smuzhiyun void ethernut5_print_version(void)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	printf("%u.%u\n", pwrman_major, pwrman_minor);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /*
124*4882a593Smuzhiyun  * All code below this point is optional and implements
125*4882a593Smuzhiyun  * the 'pwrman' command.
126*4882a593Smuzhiyun  */
127*4882a593Smuzhiyun #ifdef CONFIG_CMD_BSP
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun /* Human readable names of PMC features */
130*4882a593Smuzhiyun char *pwrman_feat[8] = {
131*4882a593Smuzhiyun 	"board", "vbin", "vbout", "mmc",
132*4882a593Smuzhiyun 	"rs232", "ethclk", "ethrst", "wakeup"
133*4882a593Smuzhiyun };
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun /*
136*4882a593Smuzhiyun  * Print all feature names, that have its related flags enabled.
137*4882a593Smuzhiyun  */
print_flagged_features(u8 flags)138*4882a593Smuzhiyun static void print_flagged_features(u8 flags)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	int i;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	for (i = 0; i < 8; i++) {
143*4882a593Smuzhiyun 		if (flags & (1 << i))
144*4882a593Smuzhiyun 			printf("%s ", pwrman_feat[i]);
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun /*
149*4882a593Smuzhiyun  * Return flags of a given list of feature names.
150*4882a593Smuzhiyun  *
151*4882a593Smuzhiyun  * The function stops at the first unknown list entry and
152*4882a593Smuzhiyun  * returns the number of detected names as a function result.
153*4882a593Smuzhiyun  */
feature_flags(char * const names[],int num,u8 * flags)154*4882a593Smuzhiyun static int feature_flags(char * const names[], int num, u8 *flags)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	int i, j;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	*flags = 0;
159*4882a593Smuzhiyun 	for (i = 0; i < num; i++) {
160*4882a593Smuzhiyun 		for (j = 0; j < 8; j++) {
161*4882a593Smuzhiyun 			if (strcmp(pwrman_feat[j], names[i]) == 0) {
162*4882a593Smuzhiyun 				*flags |= 1 << j;
163*4882a593Smuzhiyun 				break;
164*4882a593Smuzhiyun 			}
165*4882a593Smuzhiyun 		}
166*4882a593Smuzhiyun 		if (j > 7)
167*4882a593Smuzhiyun 			break;
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 	return i;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
ethernut5_print_power(void)172*4882a593Smuzhiyun void ethernut5_print_power(void)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	u8 flags;
175*4882a593Smuzhiyun 	int i;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	flags = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA);
178*4882a593Smuzhiyun 	for (i = 0; i < 2; i++) {
179*4882a593Smuzhiyun 		if (flags) {
180*4882a593Smuzhiyun 			print_flagged_features(flags);
181*4882a593Smuzhiyun 			printf("%s\n", i ? "off" : "on");
182*4882a593Smuzhiyun 		}
183*4882a593Smuzhiyun 		flags = ~flags;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
ethernut5_print_celsius(void)187*4882a593Smuzhiyun void ethernut5_print_celsius(void)
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun 	int val;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	/* Read ADC value from LM50 and return Celsius degrees. */
192*4882a593Smuzhiyun 	val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_TEMP);
193*4882a593Smuzhiyun 	val *= 5000;	/* 100mV/degree with 5V reference */
194*4882a593Smuzhiyun 	val += 128;	/* 8 bit resolution */
195*4882a593Smuzhiyun 	val /= 256;
196*4882a593Smuzhiyun 	val -= 450;	/* Celsius offset, still x10 */
197*4882a593Smuzhiyun 	/* Output full degrees. */
198*4882a593Smuzhiyun 	printf("%d\n", (val + 5) / 10);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
ethernut5_print_voltage(void)201*4882a593Smuzhiyun void ethernut5_print_voltage(void)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	int val;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	/* Read ADC value from divider and return voltage. */
206*4882a593Smuzhiyun 	val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_VAUX);
207*4882a593Smuzhiyun 	/* Resistors are 100k and 12.1k */
208*4882a593Smuzhiyun 	val += 5;
209*4882a593Smuzhiyun 	val *= 180948;
210*4882a593Smuzhiyun 	val /= 100000;
211*4882a593Smuzhiyun 	val++;
212*4882a593Smuzhiyun 	/* Calculation was done in 0.1V units. */
213*4882a593Smuzhiyun 	printf("%d\n", (val + 5) / 10);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun /*
217*4882a593Smuzhiyun  * Process the board specific 'pwrman' command.
218*4882a593Smuzhiyun  */
do_pwrman(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])219*4882a593Smuzhiyun int do_pwrman(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	u8 val;
222*4882a593Smuzhiyun 	int i;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	if (argc == 1) {
225*4882a593Smuzhiyun 		ethernut5_print_power();
226*4882a593Smuzhiyun 	} else if (argc == 2 && strcmp(argv[1], "reset") == 0) {
227*4882a593Smuzhiyun 		at91_set_pio_output(AT91_PIO_PORTB, 8, 1);
228*4882a593Smuzhiyun 		udelay(100);
229*4882a593Smuzhiyun 		at91_set_pio_output(AT91_PIO_PORTB, 8, 0);
230*4882a593Smuzhiyun 		udelay(100000);
231*4882a593Smuzhiyun 	} else if (argc == 2 && strcmp(argv[1], "temp") == 0) {
232*4882a593Smuzhiyun 		ethernut5_print_celsius();
233*4882a593Smuzhiyun 	} else if (argc == 2 && strcmp(argv[1], "vaux") == 0) {
234*4882a593Smuzhiyun 		ethernut5_print_voltage();
235*4882a593Smuzhiyun 	} else if (argc == 2 && strcmp(argv[1], "version") == 0) {
236*4882a593Smuzhiyun 		ethernut5_print_version();
237*4882a593Smuzhiyun 	} else if (strcmp(argv[1], "led") == 0) {
238*4882a593Smuzhiyun 		/* Control the green status LED. Blink frequency unit
239*4882a593Smuzhiyun 		** is 0.1s, very roughly. */
240*4882a593Smuzhiyun 		if (argc == 2) {
241*4882a593Smuzhiyun 			/* No more arguments, output current settings. */
242*4882a593Smuzhiyun 			val = i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL);
243*4882a593Smuzhiyun 			printf("led %u %u\n", val >> 4, val & 15);
244*4882a593Smuzhiyun 		} else {
245*4882a593Smuzhiyun 			/* First argument specifies the on-time. */
246*4882a593Smuzhiyun 			val = (u8) simple_strtoul(argv[2], NULL, 0);
247*4882a593Smuzhiyun 			val <<= 4;
248*4882a593Smuzhiyun 			if (argc > 3) {
249*4882a593Smuzhiyun 				/* Second argument specifies the off-time. */
250*4882a593Smuzhiyun 				val |= (u8) (simple_strtoul(argv[3], NULL, 0)
251*4882a593Smuzhiyun 						& 15);
252*4882a593Smuzhiyun 			}
253*4882a593Smuzhiyun 			/* Update the LED control register. */
254*4882a593Smuzhiyun 			i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_LEDCTL, val);
255*4882a593Smuzhiyun 		}
256*4882a593Smuzhiyun 	} else {
257*4882a593Smuzhiyun 		/* We expect a list of features followed an optional status. */
258*4882a593Smuzhiyun 		argc--;
259*4882a593Smuzhiyun 		i = feature_flags(&argv[1], argc, &val);
260*4882a593Smuzhiyun 		if (argc == i) {
261*4882a593Smuzhiyun 			/* We got a list only, print status. */
262*4882a593Smuzhiyun 			val &= i2c_reg_read(PWRMAN_I2C_ADDR, PWRMAN_REG_STA);
263*4882a593Smuzhiyun 			if (val) {
264*4882a593Smuzhiyun 				if (i > 1)
265*4882a593Smuzhiyun 					print_flagged_features(val);
266*4882a593Smuzhiyun 				printf("active\n");
267*4882a593Smuzhiyun 			} else {
268*4882a593Smuzhiyun 				printf("inactive\n");
269*4882a593Smuzhiyun 			}
270*4882a593Smuzhiyun 		} else {
271*4882a593Smuzhiyun 			/* More arguments. */
272*4882a593Smuzhiyun 			if (i == 0) {
273*4882a593Smuzhiyun 				/* No given feature, use despensibles. */
274*4882a593Smuzhiyun 				val = PWRMAN_DISPENSIBLE;
275*4882a593Smuzhiyun 			}
276*4882a593Smuzhiyun 			if (strcmp(argv[i + 1], "on") == 0) {
277*4882a593Smuzhiyun 				/* Enable features. */
278*4882a593Smuzhiyun 				i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_ENA,
279*4882a593Smuzhiyun 						val);
280*4882a593Smuzhiyun 			} else if (strcmp(argv[i + 1], "off") == 0) {
281*4882a593Smuzhiyun 				/* Disable features. */
282*4882a593Smuzhiyun 				i2c_reg_write(PWRMAN_I2C_ADDR, PWRMAN_REG_DIS,
283*4882a593Smuzhiyun 						val);
284*4882a593Smuzhiyun 			} else {
285*4882a593Smuzhiyun 				printf("Bad parameter %s\n", argv[i + 1]);
286*4882a593Smuzhiyun 				return 1;
287*4882a593Smuzhiyun 			}
288*4882a593Smuzhiyun 		}
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 	return 0;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun U_BOOT_CMD(
294*4882a593Smuzhiyun 	pwrman,	CONFIG_SYS_MAXARGS, 1, do_pwrman,
295*4882a593Smuzhiyun 	"power management",
296*4882a593Smuzhiyun 		   "- print settings\n"
297*4882a593Smuzhiyun 	"pwrman feature ...\n"
298*4882a593Smuzhiyun 	"       - print status\n"
299*4882a593Smuzhiyun 	"pwrman [feature ...] on|off\n"
300*4882a593Smuzhiyun 	"       - enable/disable specified or all dispensible features\n"
301*4882a593Smuzhiyun 	"pwrman led [on-time [off-time]]\n"
302*4882a593Smuzhiyun 	"       - print or set led blink timer\n"
303*4882a593Smuzhiyun 	"pwrman temp\n"
304*4882a593Smuzhiyun 	"       - print board temperature (Celsius)\n"
305*4882a593Smuzhiyun 	"pwrman vaux\n"
306*4882a593Smuzhiyun 	"       - print auxiliary input voltage\n"
307*4882a593Smuzhiyun 	"pwrman reset\n"
308*4882a593Smuzhiyun 	"       - reset power management controller\n"
309*4882a593Smuzhiyun 	"pwrman version\n"
310*4882a593Smuzhiyun 	"       - print firmware version\n"
311*4882a593Smuzhiyun 	"\n"
312*4882a593Smuzhiyun 	"        features, (*)=dispensible:\n"
313*4882a593Smuzhiyun 	"          board  - 1.8V and 3.3V supply\n"
314*4882a593Smuzhiyun 	"          vbin   - supply via USB device connector\n"
315*4882a593Smuzhiyun 	"          vbout  - USB host connector supply(*)\n"
316*4882a593Smuzhiyun 	"          mmc    - MMC slot supply(*)\n"
317*4882a593Smuzhiyun 	"          rs232  - RS232 driver\n"
318*4882a593Smuzhiyun 	"          ethclk - Ethernet PHY clock(*)\n"
319*4882a593Smuzhiyun 	"          ethrst - Ethernet PHY reset\n"
320*4882a593Smuzhiyun 	"          wakeup - RTC alarm"
321*4882a593Smuzhiyun );
322*4882a593Smuzhiyun #endif /* CONFIG_CMD_BSP */
323