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