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