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