diff options
author | Tom Rini <trini@konsulko.com> | 2015-05-08 10:46:59 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2015-05-08 10:46:59 -0400 |
commit | 02ffb580e6ab7aaa7f6703ed35f489e97439cb65 (patch) | |
tree | 77cea28c53d54583a3acfa5534a8aa10054eb29c | |
parent | 57cc4e64c13bc5f42cb5e8572d2c46e25cf7aea1 (diff) | |
parent | a5e1bcdeebebabdc5d013fbd488f87a4e62ff411 (diff) |
Merge git://git.denx.de/u-boot-dm
67 files changed, 1743 insertions, 649 deletions
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index 168f2efa33..b6aae3718a 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -45,11 +45,6 @@ void __udelay(unsigned long usec) os_usleep(usec); } -unsigned long __attribute__((no_instrument_function)) timer_get_us(void) -{ - return os_get_nsec() / 1000; -} - int cleanup_before_linux(void) { return 0; diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 4d5f805753..e6dd17e9ef 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -24,6 +24,7 @@ #include <asm/sections.h> #include <asm/state.h> #include <os.h> +#include <rtc_def.h> /* Operating System Interface */ @@ -537,3 +538,20 @@ int os_jump_to_image(const void *dest, int size) return unlink(fname); } + +void os_localtime(struct rtc_time *rt) +{ + time_t t = time(NULL); + struct tm *tm; + + tm = localtime(&t); + rt->tm_sec = tm->tm_sec; + rt->tm_min = tm->tm_min; + rt->tm_hour = tm->tm_hour; + rt->tm_mday = tm->tm_mday; + rt->tm_mon = tm->tm_mon + 1; + rt->tm_year = tm->tm_year + 1900; + rt->tm_wday = tm->tm_wday; + rt->tm_yday = tm->tm_yday; + rt->tm_isdst = tm->tm_isdst; +} diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index ec010402d7..b23d08b5a1 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -77,12 +77,18 @@ int sandbox_main_loop_init(void) struct sandbox_state *state = state_get_current(); /* Execute command if required */ - if (state->cmd) { - int retval; + if (state->cmd || state->run_distro_boot) { + int retval = 0; cli_init(); - retval = run_command_list(state->cmd, -1, 0); + if (state->cmd) + retval = run_command_list(state->cmd, -1, 0); + + if (state->run_distro_boot) + retval = cli_simple_run_command("run distro_bootcmd", + 0); + if (!state->interactive) os_exit(retval); } @@ -90,6 +96,14 @@ int sandbox_main_loop_init(void) return 0; } +static int sandbox_cmdline_cb_boot(struct sandbox_state *state, + const char *arg) +{ + state->run_distro_boot = true; + return 0; +} +SANDBOX_CMDLINE_OPT_SHORT(boot, 'b', 0, "Run distro boot commands"); + static int sandbox_cmdline_cb_command(struct sandbox_state *state, const char *arg) { diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index efa2097b2d..82d186ee5b 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -8,7 +8,9 @@ aliases { eth5 = "/eth@90000000"; + i2c0 = &i2c_0; pci0 = &pci; + rtc0 = &rtc_0; }; chosen { @@ -90,7 +92,7 @@ num-gpios = <10>; }; - i2c@0 { + i2c_0: i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0 0>; @@ -105,6 +107,14 @@ sandbox,size = <128>; }; }; + + rtc_0: rtc@43 { + reg = <0x43>; + compatible = "sandbox-rtc"; + emul { + compatible = "sandbox,i2c-rtc"; + }; + }; }; spi@0 { diff --git a/arch/sandbox/include/asm/eth.h b/arch/sandbox/include/asm/eth.h index 4b79ede9b9..88804fb8e2 100644 --- a/arch/sandbox/include/asm/eth.h +++ b/arch/sandbox/include/asm/eth.h @@ -12,4 +12,6 @@ void sandbox_eth_disable_response(int index, bool disable); +void sandbox_eth_skip_timeout(void); + #endif /* __ETH_H */ diff --git a/arch/sandbox/include/asm/rtc.h b/arch/sandbox/include/asm/rtc.h new file mode 100644 index 0000000000..5ed4584641 --- /dev/null +++ b/arch/sandbox/include/asm/rtc.h @@ -0,0 +1,28 @@ +/* + * Simulate an I2C real time clock + * + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __asm_rtc_h +#define __asm_rtc_h + +/* Register numbers in the sandbox RTC */ +enum { + REG_SEC = 5, + REG_MIN, + REG_HOUR, + REG_MDAY, + REG_MON, + REG_YEAR, + REG_WDAY, + + REG_RESET = 0x20, + + REG_COUNT = 0x80, +}; + +#endif diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index a0c24ba1e0..a57480a996 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -42,6 +42,7 @@ struct sandbox_spi_info { struct sandbox_state { const char *cmd; /* Command to execute */ bool interactive; /* Enable cmdline after execute */ + bool run_distro_boot; /* Automatically run distro bootcommands */ const char *fdt_fname; /* Filename of FDT binary */ const char *parse_err; /* Error to report from parsing */ int argc; /* Program arguments */ diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 8e490e96d7..91a5c79ad2 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -17,6 +17,16 @@ #define SANDBOX_PCI_CLASS_CODE PCI_CLASS_CODE_COMM #define SANDBOX_PCI_CLASS_SUB_CODE PCI_CLASS_SUB_CODE_COMM_SERIAL +/** + * sandbox_i2c_set_test_mode() - set test mode for running unit tests + * + * See sandbox_i2c_xfer() for the behaviour changes. + * + * @bus: sandbox I2C bus to adjust + * @test_mode: true to select test mode, false to run normally + */ +void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode); + enum sandbox_i2c_eeprom_test_mode { SIE_TEST_MODE_NONE, /* Permits read/write of only one byte per I2C transaction */ @@ -28,4 +38,33 @@ void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len); +/* + * sandbox_timer_add_offset() + * + * Allow tests to add to the time reported through lib/time.c functions + * offset: number of milliseconds to advance the system time + */ +void sandbox_timer_add_offset(unsigned long offset); + +/** + * sandbox_i2c_rtc_set_offset() - set the time offset from system/base time + * + * @dev: RTC device to adjust + * @use_system_time: true to use system time, false to use @base_time + * @offset: RTC offset from current system/base time (-1 for no + * change) + * @return old value of RTC offset + */ +long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, + int offset); + +/** + * sandbox_i2c_rtc_get_set_base_time() - get and set the base time + * + * @dev: RTC device to adjust + * @base_time: New base system time (set to -1 for no change) + * @return old base time + */ +long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time); + #endif diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 2227f1c121..80eaa6334c 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -7,6 +7,7 @@ #include <cros_ec.h> #include <dm.h> #include <os.h> +#include <asm/test.h> #include <asm/u-boot-sandbox.h> /* @@ -25,9 +26,17 @@ void flush_cache(unsigned long start, unsigned long size) { } +/* system timer offset in ms */ +static unsigned long sandbox_timer_offset; + +void sandbox_timer_add_offset(unsigned long offset) +{ + sandbox_timer_offset += offset; +} + unsigned long timer_read_counter(void) { - return os_get_nsec() / 1000; + return os_get_nsec() / 1000 + sandbox_timer_offset * 1000; } int dram_init(void) diff --git a/common/cmd_date.c b/common/cmd_date.c index 4a653e5bcf..61727e3d1f 100644 --- a/common/cmd_date.c +++ b/common/cmd_date.c @@ -10,6 +10,7 @@ */ #include <common.h> #include <command.h> +#include <dm.h> #include <rtc.h> #include <i2c.h> @@ -33,10 +34,18 @@ static int do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct rtc_time tm; int rcode = 0; - int old_bus; + int old_bus __maybe_unused; /* switch to correct I2C bus */ -#ifdef CONFIG_SYS_I2C +#ifdef CONFIG_DM_I2C + struct udevice *dev; + + rcode = uclass_get_device(UCLASS_RTC, 0, &dev); + if (rcode) { + printf("Cannot find RTC: err=%d\n", rcode); + return CMD_RET_FAILURE; + } +#elif defined(CONFIG_SYS_I2C) old_bus = i2c_get_bus_num(); i2c_set_bus_num(CONFIG_SYS_RTC_BUS_NUM); #else @@ -48,32 +57,50 @@ static int do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) case 2: /* set date & time */ if (strcmp(argv[1],"reset") == 0) { puts ("Reset RTC...\n"); - rtc_reset (); +#ifdef CONFIG_DM_I2C + rcode = dm_rtc_reset(dev); + if (!rcode) + rcode = dm_rtc_set(dev, &default_tm); +#else + rtc_reset(); rcode = rtc_set(&default_tm); +#endif if (rcode) puts("## Failed to set date after RTC reset\n"); } else { /* initialize tm with current time */ - rcode = rtc_get (&tm); - - if(!rcode) { +#ifdef CONFIG_DM_I2C + rcode = dm_rtc_get(dev, &tm); +#else + rcode = rtc_get(&tm); +#endif + if (!rcode) { /* insert new date & time */ - if (mk_date (argv[1], &tm) != 0) { + if (mk_date(argv[1], &tm) != 0) { puts ("## Bad date format\n"); break; } /* and write to RTC */ - rcode = rtc_set (&tm); - if(rcode) - puts("## Set date failed\n"); +#ifdef CONFIG_DM_I2C + rcode = dm_rtc_set(dev, &tm); +#else + rcode = rtc_set(&tm); +#endif + if (rcode) { + printf("## Set date failed: err=%d\n", + rcode); + } } else { puts("## Get date failed\n"); } } /* FALL TROUGH */ case 1: /* get date & time */ - rcode = rtc_get (&tm); - +#ifdef CONFIG_DM_I2C + rcode = dm_rtc_get(dev, &tm); +#else + rcode = rtc_get(&tm); +#endif if (rcode) { puts("## Get date failed\n"); break; @@ -93,11 +120,11 @@ static int do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /* switch back to original I2C bus */ #ifdef CONFIG_SYS_I2C i2c_set_bus_num(old_bus); -#else +#elif !defined(CONFIG_DM_I2C) I2C_SET_BUS(old_bus); #endif - return rcode; + return rcode ? CMD_RET_FAILURE : 0; } /* @@ -201,7 +228,7 @@ int mk_date (const char *datestr, struct rtc_time *tmp) tmp->tm_min = val; /* calculate day of week */ - GregorianDay (tmp); + rtc_calc_weekday(tmp); return (0); default: diff --git a/common/fdt_support.c b/common/fdt_support.c index c5ed5ad89e..9e50148462 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -1533,7 +1533,7 @@ int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width, if (ret < 0) return ret; - snprintf(name, sizeof(name), "framebuffer@%llx", base_address); + snprintf(name, sizeof(name), "framebuffer@%" PRIx64, base_address); ret = fdt_set_name(fdt, node, name); if (ret < 0) return ret; diff --git a/common/image.c b/common/image.c index abc0d890f2..fdec496c4b 100644 --- a/common/image.c +++ b/common/image.c @@ -533,7 +533,7 @@ void genimg_print_time(time_t timestamp) #ifndef USE_HOSTCC struct rtc_time tm; - to_tm(timestamp, &tm); + rtc_to_tm(timestamp, &tm); printf("%4d-%02d-%02d %2d:%02d:%02d UTC\n", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); diff --git a/common/usb.c b/common/usb.c index a4820d3e94..1b26bfab36 100644 --- a/common/usb.c +++ b/common/usb.c @@ -946,13 +946,18 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) * send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is * only 18 bytes long, this will terminate with a short packet. But if * the maxpacket size is 8 or 16 the device may be waiting to transmit - * some more, or keeps on retransmitting the 8 byte header. */ + * some more, or keeps on retransmitting the 8 byte header. + */ - dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */ - /* Default to 64 byte max packet size */ - dev->maxpacketsize = PACKET_SIZE_64; - dev->epmaxpacketin[0] = 64; - dev->epmaxpacketout[0] = 64; + if (dev->speed == USB_SPEED_LOW) { + dev->descriptor.bMaxPacketSize0 = 8; + dev->maxpacketsize = PACKET_SIZE_8; + } else { + dev->descriptor.bMaxPacketSize0 = 64; + dev->maxpacketsize = PACKET_SIZE_64; + } + dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; if (do_read) { int err; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5de7fbedb4..70ae5313b6 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -26,3 +26,5 @@ CONFIG_TPM_TIS_SANDBOX=y CONFIG_SOUND=y CONFIG_CMD_SOUND=y CONFIG_SOUND_SANDBOX=y +CONFIG_DM_RTC=y +CONFIG_CMD_UT_TIME=y diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index ed899d4777..a8b83c51bc 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -41,18 +41,19 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda) static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) { - if (bit) { + if (bit) dm_gpio_set_dir_flags(sda, GPIOD_IS_IN); - } else { + else dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT); - dm_gpio_set_value(sda, 0); - } } static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit) { - dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT); - dm_gpio_set_value(scl, bit); + ulong flags = GPIOD_IS_OUT; + + if (bit) + flags |= GPIOD_IS_OUT_ACTIVE; + dm_gpio_set_dir_flags(scl, flags); } static void i2c_gpio_write_bit(struct gpio_desc *scl, struct gpio_desc *sda, diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index f2e95c0881..b8eb2d613f 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -186,6 +186,25 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, } } +int dm_i2c_reg_read(struct udevice *dev, uint offset) +{ + uint8_t val; + int ret; + + ret = dm_i2c_read(dev, offset, &val, 1); + if (ret < 0) + return ret; + + return val; +} + +int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value) +{ + uint8_t val = value; + + return dm_i2c_write(dev, offset, &val, 1); +} + /** * i2c_probe_chip() - probe for a chip on a bus * diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index d6adc0f721..dd1c7e59e4 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -18,8 +18,8 @@ DECLARE_GLOBAL_DATA_PTR; -struct dm_sandbox_i2c_emul_priv { - struct udevice *emul; +struct sandbox_i2c_priv { + bool test_mode; }; static int get_emul(struct udevice *dev, struct udevice **devp, @@ -47,17 +47,25 @@ static int get_emul(struct udevice *dev, struct udevice **devp, return 0; } +void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode) +{ + struct sandbox_i2c_priv *priv = dev_get_priv(bus); + + priv->test_mode = test_mode; +} + static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) { struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); + struct sandbox_i2c_priv *priv = dev_get_priv(bus); struct dm_i2c_ops *ops; struct udevice *emul, *dev; bool is_read; int ret; /* Special test code to return success but with no emulation */ - if (msg->addr == SANDBOX_I2C_TEST_ADDR) + if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR) return 0; ret = i2c_get_chip(bus, msg->addr, 1, &dev); @@ -68,13 +76,18 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, if (ret) return ret; - /* - * For testing, don't allow writing above 100KHz for writes and - * 400KHz for reads - */ - is_read = nmsgs > 1; - if (i2c->speed_hz > (is_read ? 400000 : 100000)) - return -EINVAL; + if (priv->test_mode) { + /* + * For testing, don't allow writing above 100KHz for writes and + * 400KHz for reads. + */ + is_read = nmsgs > 1; + if (i2c->speed_hz > (is_read ? 400000 : 100000)) { + debug("%s: Max speed exceeded\n", __func__); + return -EINVAL; + } + } + return ops->xfer(emul, msg, nmsgs); } @@ -92,4 +105,5 @@ U_BOOT_DRIVER(i2c_sandbox) = { .id = UCLASS_I2C, .of_match = sandbox_i2c_ids, .ops = &sandbox_i2c_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv), }; diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 982bac788d..4b6ac6a6c0 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -986,7 +986,8 @@ int cros_ec_register(struct udevice *dev) } /* Remember this device for use by the cros_ec command */ - debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id); + debug("Google Chrome EC v%d CROS-EC driver ready, id '%s'\n", + cdev->protocol_version, id); return 0; } diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index ac2ee86eda..0686925aaf 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -25,6 +25,8 @@ int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes) { struct cros_ec_dev *dev = dev_get_uclass_priv(udev); struct spi_slave *slave = dev_get_parentdata(dev->dev); + ulong start; + uint8_t byte; int rv; /* Do the transfer */ @@ -33,10 +35,25 @@ int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes) return -1; } - rv = spi_xfer(slave, max(out_bytes, in_bytes) * 8, - dev->dout, dev->din, - SPI_XFER_BEGIN | SPI_XFER_END); + rv = spi_xfer(slave, out_bytes * 8, dev->dout, NULL, SPI_XFER_BEGIN); + if (rv) + goto done; + start = get_timer(0); + while (1) { + rv = spi_xfer(slave, 8, NULL, &byte, 0); + if (byte == SPI_PREAMBLE_END_BYTE) + break; + if (rv) + goto done; + if (get_timer(start) > 100) { + rv = -ETIMEDOUT; + goto done; + } + } + rv = spi_xfer(slave, in_bytes * 8, NULL, dev->din, 0); +done: + spi_xfer(slave, 0, NULL, NULL, SPI_XFER_END); spi_release_bus(slave); if (rv) { diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c index 4b25902b8d..350e21aa7d 100644 --- a/drivers/mtd/spi/sf-uclass.c +++ b/drivers/mtd/spi/sf-uclass.c @@ -53,10 +53,10 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, { struct spi_slave *slave; struct udevice *bus; - char name[20], *str; + char name[30], *str; int ret; - snprintf(name, sizeof(name), "%d:%d", busnum, cs); + snprintf(name, sizeof(name), "spi_flash@%d:%d", busnum, cs); str = strdup(name); ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode, "spi_flash_std", str, &bus, &slave); diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c index e239ff4447..4e083d32ae 100644 --- a/drivers/net/sandbox.c +++ b/drivers/net/sandbox.c @@ -11,6 +11,7 @@ #include <dm.h> #include <malloc.h> #include <net.h> +#include <asm/test.h> DECLARE_GLOBAL_DATA_PTR; @@ -30,6 +31,7 @@ struct eth_sandbox_priv { }; static bool disabled[8] = {false}; +static bool skip_timeout; /* * sandbox_eth_disable_response() @@ -42,6 +44,16 @@ void sandbox_eth_disable_response(int index, bool disable) disabled[index] = disable; } +/* + * sandbox_eth_skip_timeout() + * + * When the first packet read is attempted, fast-forward time + */ +void sandbox_eth_skip_timeout(void) +{ + skip_timeout = true; +} + static int sb_eth_start(struct udevice *dev) { struct eth_sandbox_priv *priv = dev_get_priv(dev); @@ -144,6 +156,11 @@ static int sb_eth_recv(struct udevice *dev, uchar **packetp) { struct eth_sandbox_priv *priv = dev_get_priv(dev); + if (skip_timeout) { + sandbox_timer_add_offset(10000UL); + skip_timeout = false; + } + if (priv->recv_packet_length) { int lcl_recv_packet_length = priv->recv_packet_length; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e69de29bb2..bd63621e37 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -0,0 +1,8 @@ +config DM_RTC + bool "Enable Driver Model for RTC drivers" + depends on DM + help + Enable drver model for real-time-clock drivers. The RTC uclass + then provides the rtc_get()/rtc_set() interface, delegating to + drivers to perform the actual functions. See rtc.h for a + description of the API. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fdcbc00295..3092de1d9c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -7,6 +7,8 @@ #ccflags-y += -DDEBUG +obj-$(CONFIG_DM_RTC) += rtc-uclass.o + obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o obj-$(CONFIG_RTC_BFIN) += bfin_rtc.o obj-y += date.o @@ -24,6 +26,7 @@ obj-$(CONFIG_RTC_DS164x) += ds164x.o obj-$(CONFIG_RTC_DS174x) += ds174x.o obj-$(CONFIG_RTC_DS3231) += ds3231.o obj-$(CONFIG_RTC_FTRTC010) += ftrtc010.o +obj-$(CONFIG_SANDBOX) += i2c_rtc_emul.o obj-$(CONFIG_RTC_IMXDI) += imxdi.o obj-$(CONFIG_RTC_ISL1208) += isl1208.o obj-$(CONFIG_RTC_M41T11) += m41t11.o @@ -49,4 +52,5 @@ obj-$(CONFIG_RTC_RTC4543) += rtc4543.o obj-$(CONFIG_RTC_RV3029) += rv3029.o obj-$(CONFIG_RTC_RX8025) += rx8025.o obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o +obj-$(CONFIG_SANDBOX) += sandbox_rtc.o obj-$(CONFIG_RTC_X1205) += x1205.o diff --git a/drivers/rtc/at91sam9_rtt.c b/drivers/rtc/at91sam9_rtt.c index 714dd2a34f..a684ad6a6f 100644 --- a/drivers/rtc/at91sam9_rtt.c +++ b/drivers/rtc/at91sam9_rtt.c @@ -44,7 +44,7 @@ int rtc_get (struct rtc_time *tmp) } while (tim!=tim2); off = readl(&gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]); /* off==0 means time is invalid, but we ignore that */ - to_tm (tim+off, tmp); + rtc_to_tm(tim+off, tmp); return 0; } @@ -54,8 +54,7 @@ int rtc_set (struct rtc_time *tmp) at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; ulong tim; - tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + tim = rtc_mktime(tmp); /* clear alarm, set prescaler to 32768, clear counter */ writel(32768+AT91_RTT_RTTRST, &rtt->mr); diff --git a/drivers/rtc/bfin_rtc.c b/drivers/rtc/bfin_rtc.c index 4cf2d834b2..a079a1d472 100644 --- a/drivers/rtc/bfin_rtc.c +++ b/drivers/rtc/bfin_rtc.c @@ -67,8 +67,7 @@ int rtc_set(struct rtc_time *tmp) wait_for_complete(); /* Calculate number of seconds this incoming time represents */ - remain = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + remain = rtc_mktime(tmp); /* Figure out how many days since epoch */ days = remain / NUM_SECS_IN_DAY; @@ -114,7 +113,7 @@ int rtc_get(struct rtc_time *tmp) /* Calculate the total number of seconds since epoch */ time_in_sec = (tm_sec) + MIN_TO_SECS(tm_min) + HRS_TO_SECS(tm_hr) + DAYS_TO_SECS(tm_day); - to_tm(time_in_sec, tmp); + rtc_to_tm(time_in_sec, tmp); return 0; } diff --git a/drivers/rtc/date.c b/drivers/rtc/date.c index 15e6db06b2..8c643a0b46 100644 --- a/drivers/rtc/date.c +++ b/drivers/rtc/date.c @@ -11,6 +11,7 @@ #include <common.h> #include <command.h> +#include <errno.h> #include <rtc.h> #if defined(CONFIG_CMD_DATE) || defined(CONFIG_TIMESTAMP) @@ -30,13 +31,15 @@ static int month_days[12] = { /* * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) */ -void GregorianDay(struct rtc_time * tm) +int rtc_calc_weekday(struct rtc_time *tm) { int leapsToDate; int lastYear; int day; int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 }; + if (tm->tm_year < 1753) + return -EINVAL; lastYear=tm->tm_year-1; /* @@ -64,9 +67,11 @@ void GregorianDay(struct rtc_time * tm) day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; tm->tm_wday=day%7; + + return 0; } -void to_tm(int tim, struct rtc_time * tm) +int rtc_to_tm(int tim, struct rtc_time *tm) { register int i; register long hms, day; @@ -98,10 +103,14 @@ void to_tm(int tim, struct rtc_time * tm) /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; + /* Zero unused fields */ + tm->tm_yday = 0; + tm->tm_isdst = 0; + /* * Determine the day of week */ - GregorianDay(tm); + return rtc_calc_weekday(tm); } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. @@ -119,22 +128,23 @@ void to_tm(int tim, struct rtc_time * tm) * machines were long is 32-bit! (However, as time_t is signed, we * will already get problems at other places on 2038-01-19 03:14:08) */ -unsigned long -mktime (unsigned int year, unsigned int mon, - unsigned int day, unsigned int hour, - unsigned int min, unsigned int sec) +unsigned long rtc_mktime(const struct rtc_time *tm) { - if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + int mon = tm->tm_mon; + int year = tm->tm_year; + int days, hours; + + mon -= 2; + if (0 >= (int)mon) { /* 1..12 -> 11,12,1..10 */ mon += 12; /* Puts Feb last since it has leap day */ year -= 1; } - return ((( - (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) + - year*365 - 719499 - )*24 + hour /* now have hours */ - )*60 + min /* now have minutes */ - )*60 + sec; /* finally seconds */ + days = (unsigned long)(year / 4 - year / 100 + year / 400 + + 367 * mon / 12 + tm->tm_mday) + + year * 365 - 719499; + hours = days * 24 + tm->tm_hour; + return (hours * 60 + tm->tm_min) * 60 + tm->tm_sec; } #endif diff --git a/drivers/rtc/ds1306.c b/drivers/rtc/ds1306.c index 1ec1837cb4..7dd3e19028 100644 --- a/drivers/rtc/ds1306.c +++ b/drivers/rtc/ds1306.c @@ -110,7 +110,7 @@ int rtc_get (struct rtc_time *tmp) immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ udelay (10); - GregorianDay (tmp); /* Determine the day of week */ + rtc_calc_weekday(tmp); /* Determine the day of week */ debug ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, @@ -180,8 +180,7 @@ int rtc_set (struct rtc_time *tmp) { ulong tim; - tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + tim = rtc_mktime(tmp); immap->im_sitk.sitk_rtck = KAPWR_KEY; immap->im_sit.sit_rtc = tim; diff --git a/drivers/rtc/ds1374.c b/drivers/rtc/ds1374.c index 427b1eb8d0..78473570b9 100644 --- a/drivers/rtc/ds1374.c +++ b/drivers/rtc/ds1374.c @@ -118,7 +118,7 @@ int rtc_get (struct rtc_time *tm){ DEBUGR ("Get RTC s since 1.1.1970: %ld\n", time1); - to_tm(time1, tm); /* To Gregorian Date */ + rtc_to_tm(time1, tm); /* To Gregorian Date */ if (rtc_read(RTC_SR_ADDR) & RTC_SR_BIT_OSF) { printf ("### Warning: RTC oscillator has stopped\n"); @@ -147,9 +147,7 @@ int rtc_set (struct rtc_time *tmp){ if (tmp->tm_year < 1970 || tmp->tm_year > 2069) printf("WARNING: year should be between 1970 and 2069!\n"); - time = mktime(tmp->tm_year, tmp->tm_mon, - tmp->tm_mday, tmp->tm_hour, - tmp->tm_min, tmp->tm_sec); + time = rtc_mktime(tmp); DEBUGR ("Set RTC s since 1.1.1970: %ld (0x%02lx)\n", time, time); diff --git a/drivers/rtc/ftrtc010.c b/drivers/rtc/ftrtc010.c index 713dad274f..7d0cfb3ba8 100644 --- a/drivers/rtc/ftrtc010.c +++ b/drivers/rtc/ftrtc010.c @@ -86,7 +86,7 @@ int rtc_get(struct rtc_time *tmp) now = ftrtc010_time() + readl(&rtc->record); #endif - to_tm(now, tmp); + rtc_to_tm(now, tmp); return 0; } @@ -104,8 +104,7 @@ int rtc_set(struct rtc_time *tmp) tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - new = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_hour, - tmp->tm_min, tmp->tm_sec); + new = rtc_mktime(tmp); now = ftrtc010_time(); diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c new file mode 100644 index 0000000000..20827fdff1 --- /dev/null +++ b/drivers/rtc/i2c_rtc_emul.c @@ -0,0 +1,236 @@ +/* + * Simulate an I2C real time clock + * + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This is a test driver. It starts off with the current time of the machine, + * but also supports setting the time, using an offset from the current + * clock. This driver is only intended for testing, not accurate + * time-keeping. It does not change the system time. + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> +#include <os.h> +#include <rtc.h> +#include <asm/rtc.h> +#include <asm/test.h> + +#ifdef DEBUG +#define debug_buffer print_buffer +#else +#define debug_buffer(x, ...) +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct sandbox_i2c_rtc_plat_data - platform data for the RTC + * + * @base_time: Base system time when RTC device was bound + * @offset: RTC offset from current system time + * @use_system_time: true to use system time, false to use @base_time + * @reg: Register values + */ +struct sandbox_i2c_rtc_plat_data { + long base_time; + long offset; + bool use_system_time; + u8 reg[REG_COUNT]; +}; + +struct sandbox_i2c_rtc { + unsigned int offset_secs; +}; + +long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, + int offset) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); + long old_offset; + + old_offset = plat->offset; + plat->use_system_time = use_system_time; + if (offset != -1) + plat->offset = offset; + + return old_offset; +} + +long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); + long old_base_time; + + old_base_time = plat->base_time; + if (base_time != -1) + plat->base_time = base_time; + + return old_base_time; +} + +static void reset_time(struct udevice *dev) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); + struct rtc_time now; + + os_localtime(&now); + plat->base_time = rtc_mktime(&now); + plat->offset = 0; + plat->use_system_time = true; +} + +static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); + struct rtc_time tm_now; + long now; + + if (plat->use_system_time) { + os_localtime(&tm_now); + now = rtc_mktime(&tm_now); + } else { + now = plat->base_time; + } + + return rtc_to_tm(now + plat->offset, time); +} + +static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); + struct rtc_time tm_now; + long now; + + if (plat->use_system_time) { + os_localtime(&tm_now); + now = rtc_mktime(&tm_now); + } else { + now = plat->base_time; + } + plat->offset = rtc_mktime(time) - now; + + return 0; +} + +/* Update the current time in the registers */ +static int sandbox_i2c_rtc_prepare_read(struct udevice *emul) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); + struct rtc_time time; + int ret; + + ret = sandbox_i2c_rtc_get(emul, &time); + if (ret) + return ret; + + plat->reg[REG_SEC] = time.tm_sec; + plat->reg[REG_MIN] = time.tm_min; + plat->reg[REG_HOUR] = time.tm_hour; + plat->reg[REG_MDAY] = time.tm_mday; + plat->reg[REG_MON] = time.tm_mon; + plat->reg[REG_YEAR] = time.tm_year - 1900; + plat->reg[REG_WDAY] = time.tm_wday; + + return 0; +} + +static int sandbox_i2c_rtc_complete_write(struct udevice *emul) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); + struct rtc_time time; + int ret; + + time.tm_sec = plat->reg[REG_SEC]; + time.tm_min = plat->reg[REG_MIN]; + time.tm_hour = plat->reg[REG_HOUR]; + time.tm_mday = plat->reg[REG_MDAY]; + time.tm_mon = plat->reg[REG_MON]; + time.tm_year = plat->reg[REG_YEAR] + 1900; + time.tm_wday = plat->reg[REG_WDAY]; + + ret = sandbox_i2c_rtc_set(emul, &time); + if (ret) + return ret; + + return 0; +} + +static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg, + int nmsgs) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); + uint offset = 0; + int ret; + + debug("\n%s\n", __func__); + ret = sandbox_i2c_rtc_prepare_read(emul); + if (ret) + return ret; + for (; nmsgs > 0; nmsgs--, msg++) { + int len; + u8 *ptr; + + len = msg->len; + debug(" %s: msg->len=%d", + msg->flags & I2C_M_RD ? "read" : "write", + msg->len); + if (msg->flags & I2C_M_RD) { + debug(", offset %x, len %x: ", offset, len); + + /* Read the register */ + memcpy(msg->buf, plat->reg + offset, len); + memset(msg->buf + len, '\xff', msg->len - len); + debug_buffer(0, msg->buf, 1, msg->len, 0); + } else if (len >= 1) { + ptr = msg->buf; + offset = *ptr++ & (REG_COUNT - 1); + len--; + debug(", set offset %x: ", offset); + debug_buffer(0, msg->buf, 1, msg->len, 0); + + /* Write the register */ + memcpy(plat->reg + offset, ptr, len); + if (offset == REG_RESET) + reset_time(emul); + } + } + ret = sandbox_i2c_rtc_complete_write(emul); + if (ret) + return ret; + + return 0; +} + +struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = { + .xfer = sandbox_i2c_rtc_xfer, +}; + +static int sandbox_i2c_rtc_bind(struct udevice *dev) +{ + reset_time(dev); + + return 0; +} + +static const struct udevice_id sandbox_i2c_rtc_ids[] = { + { .compatible = "sandbox,i2c-rtc" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = { + .name = "sandbox_i2c_rtc_emul", + .id = UCLASS_I2C_EMUL, + .of_match = sandbox_i2c_rtc_ids, + .bind = sandbox_i2c_rtc_bind, + .priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc), + .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data), + .ops = &sandbox_i2c_rtc_emul_ops, +}; diff --git a/drivers/rtc/imxdi.c b/drivers/rtc/imxdi.c index 0d7d736eff..17519ce2c0 100644 --- a/drivers/rtc/imxdi.c +++ b/drivers/rtc/imxdi.c @@ -192,7 +192,7 @@ int rtc_get(struct rtc_time *tmp) } now = __raw_readl(&data.regs->dtcmr); - to_tm(now, tmp); + rtc_to_tm(now, tmp); err: return rc; @@ -209,8 +209,7 @@ int rtc_set(struct rtc_time *tmp) goto err; } - now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + now = rtc_mktime(tmp); /* zero the fractional part first */ rc = DI_WRITE_WAIT(0, dtclr); if (rc == 0) diff --git a/drivers/rtc/mc13xxx-rtc.c b/drivers/rtc/mc13xxx-rtc.c index 528247ac86..3e463368b0 100644 --- a/drivers/rtc/mc13xxx-rtc.c +++ b/drivers/rtc/mc13xxx-rtc.c @@ -36,7 +36,7 @@ int rtc_get(struct rtc_time *rtc) tim = day1 * 86400 + time; - to_tm(tim, rtc); + rtc_to_tm(tim, rtc); rtc->tm_yday = 0; rtc->tm_isdst = 0; @@ -51,8 +51,7 @@ int rtc_set(struct rtc_time *rtc) if (!p) return -1; - time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday, - rtc->tm_hour, rtc->tm_min, rtc->tm_sec); + time = rtc_mktime(rtc); day = time / 86400; time %= 86400; diff --git a/drivers/rtc/mcfrtc.c b/drivers/rtc/mcfrtc.c index 8961ca4f8b..e02e29793e 100644 --- a/drivers/rtc/mcfrtc.c +++ b/drivers/rtc/mcfrtc.c @@ -38,7 +38,7 @@ int rtc_get(struct rtc_time *tmp) tim = (tim * 60) + rtc_mins; tim = (tim * 60) + rtc->seconds; - to_tm(tim, tmp); + rtc_to_tm(tim, tmp); tmp->tm_yday = 0; tmp->tm_isdst = 0; diff --git a/drivers/rtc/mpc8xx.c b/drivers/rtc/mpc8xx.c index d239daee1b..147a225c6b 100644 --- a/drivers/rtc/mpc8xx.c +++ b/drivers/rtc/mpc8xx.c @@ -26,7 +26,7 @@ int rtc_get (struct rtc_time *tmp) tim = immr->im_sit.sit_rtc; - to_tm (tim, tmp); + rtc_to_tm(tim, tmp); debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, @@ -44,8 +44,7 @@ int rtc_set (struct rtc_time *tmp) tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + tim = rtc_mktime(tmp); immr->im_sitk.sitk_rtck = KAPWR_KEY; immr->im_sit.sit_rtc = tim; diff --git a/drivers/rtc/mx27rtc.c b/drivers/rtc/mx27rtc.c index ae6595b860..29ccdf1730 100644 --- a/drivers/rtc/mx27rtc.c +++ b/drivers/rtc/mx27rtc.c @@ -30,7 +30,7 @@ int rtc_get(struct rtc_time *time) sec += min * 60 + hour * 3600 + day * 24 * 3600; - to_tm(sec, time); + rtc_to_tm(sec, time); return 0; } @@ -40,8 +40,7 @@ int rtc_set(struct rtc_time *time) struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; uint32_t day, hour, min, sec; - sec = mktime(time->tm_year, time->tm_mon, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); + sec = rtc_mktime(time); day = sec / (24 * 3600); sec = sec % (24 * 3600); diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c index 32ba8a3062..6e32154f47 100644 --- a/drivers/rtc/mxsrtc.c +++ b/drivers/rtc/mxsrtc.c @@ -43,7 +43,7 @@ int rtc_get(struct rtc_time *time) uint32_t secs; secs = readl(&rtc_regs->hw_rtc_seconds); - to_tm(secs, time); + rtc_to_tm(secs, time); return 0; } @@ -52,8 +52,7 @@ int rtc_set(struct rtc_time *time) { uint32_t secs; - secs = mktime(time->tm_year, time->tm_mon, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); + secs = rtc_mktime(time); return mxs_rtc_set_time(secs); } diff --git a/drivers/rtc/pl031.c b/drivers/rtc/pl031.c index c4d1259a89..fc83049ecd 100644 --- a/drivers/rtc/pl031.c +++ b/drivers/rtc/pl031.c @@ -72,8 +72,7 @@ int rtc_set(struct rtc_time *tmp) } /* Calculate number of seconds this incoming time represents */ - tim = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + tim = rtc_mktime(tmp); RTC_WRITE_REG(RTC_LR, tim); @@ -97,7 +96,7 @@ int rtc_get(struct rtc_time *tmp) tim = RTC_READ_REG(RTC_DR); - to_tm (tim, tmp); + rtc_to_tm(tim, tmp); debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, diff --git a/drivers/rtc/rtc-uclass.c b/drivers/rtc/rtc-uclass.c new file mode 100644 index 0000000000..fe74c69f97 --- /dev/null +++ b/drivers/rtc/rtc-uclass.c @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <rtc.h> + +int dm_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (!ops->get) + return -ENOSYS; + return ops->get(dev, time); +} + +int dm_rtc_set(struct udevice *dev, struct rtc_time *time) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (!ops->set) + return -ENOSYS; + return ops->set(dev, time); +} + +int dm_rtc_reset(struct udevice *dev) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (!ops->reset) + return -ENOSYS; + return ops->reset(dev); +} + +int rtc_read8(struct udevice *dev, unsigned int reg) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (!ops->read8) + return -ENOSYS; + return ops->read8(dev, reg); +} + +int rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (!ops->write8) + return -ENOSYS; + return ops->write8(dev, reg, val); +} + +int rtc_read32(struct udevice *dev, unsigned int reg, u32 *valuep) +{ + u32 value = 0; + int ret; + int i; + + for (i = 0; i < sizeof(value); i++) { + ret = rtc_read8(dev, reg + i); + if (ret) + return ret; + value |= ret << (i << 3); + } + + *valuep = value; + return 0; +} + +int rtc_write32(struct udevice *dev, unsigned int reg, u32 value) +{ + int i, ret; + + for (i = 0; i < sizeof(value); i++) { + ret = rtc_write8(dev, reg + i, (value >> (i << 3)) & 0xff); + if (ret) + return ret; + } + + return 0; +} + +UCLASS_DRIVER(rtc) = { + .name = "rtc", + .id = UCLASS_RTC, +}; diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c new file mode 100644 index 0000000000..f292fbe9b6 --- /dev/null +++ b/drivers/rtc/sandbox_rtc.c @@ -0,0 +1,106 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <asm/rtc.h> + +#define REG_COUNT 0x80 + +static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + time->tm_sec = dm_i2c_reg_read(dev, REG_SEC); + if (time->tm_sec < 0) + return time->tm_sec; + time->tm_min = dm_i2c_reg_read(dev, REG_MIN); + if (time->tm_min < 0) + return time->tm_min; + time->tm_hour = dm_i2c_reg_read(dev, REG_HOUR); + if (time->tm_hour < 0) + return time->tm_hour; + time->tm_mday = dm_i2c_reg_read(dev, REG_MDAY); + if (time->tm_mday < 0) + return time->tm_mday; + time->tm_mon = dm_i2c_reg_read(dev, REG_MON); + if (time->tm_mon < 0) + return time->tm_mon; + time->tm_year = dm_i2c_reg_read(dev, REG_YEAR); + if (time->tm_year < 0) + return time->tm_year; + time->tm_year += 1900; + time->tm_wday = dm_i2c_reg_read(dev, REG_WDAY); + if (time->tm_wday < 0) + return time->tm_wday; + + return 0; +} + +static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time) +{ + int ret; + + ret = dm_i2c_reg_write(dev, REG_SEC, time->tm_sec); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_MIN, time->tm_min); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_HOUR, time->tm_hour); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_MDAY, time->tm_mday); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_MON, time->tm_mon); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_YEAR, time->tm_year - 1900); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_WDAY, time->tm_wday); + if (ret < 0) + return ret; + + return 0; +} + +static int sandbox_rtc_reset(struct udevice *dev) +{ + return dm_i2c_reg_write(dev, REG_RESET, 0); +} + +static int sandbox_rtc_read8(struct udevice *dev, unsigned int reg) +{ + return dm_i2c_reg_read(dev, reg); +} + +static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + return dm_i2c_reg_write(dev, reg, val); +} + +static const struct rtc_ops sandbox_rtc_ops = { + .get = sandbox_rtc_get, + .set = sandbox_rtc_set, + .reset = sandbox_rtc_reset, + .read8 = sandbox_rtc_read8, + .write8 = sandbox_rtc_write8, +}; + +static const struct udevice_id sandbox_rtc_ids[] = { + { .compatible = "sandbox-rtc" }, + { } +}; + +U_BOOT_DRIVER(rtc_sandbox) = { + .name = "rtc-sandbox", + .id = UCLASS_RTC, + .of_match = sandbox_rtc_ids, + .ops = &sandbox_rtc_ops, +}; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 83fe8e0d69..737ae64052 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -63,9 +63,12 @@ int spi_claim_bus(struct spi_slave *slave) } if (!speed) speed = 100000; - ret = spi_set_speed_mode(bus, speed, slave->mode); - if (ret) - return ret; + if (speed != slave->speed) { + ret = spi_set_speed_mode(bus, speed, slave->mode); + if (ret) + return ret; + slave->speed = speed; + } return ops->claim_bus ? ops->claim_bus(dev) : 0; } diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 86cf6312fe..18e9251b64 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -25,14 +25,12 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_USB struct exynos_ehci_platdata { struct usb_platdata usb_plat; fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif /** * Contains pointers to register base addresses @@ -42,16 +40,8 @@ struct exynos_ehci { struct ehci_ctrl ctrl; struct exynos_usb_phy *usb; struct ehci_hccr *hcd; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif }; -#ifndef CONFIG_DM_USB -static struct exynos_ehci exynos; -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -91,55 +81,6 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) return 0; } -#else -static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI); - if (node <= 0) { - debug("EHCI: Can't get device node for ehci\n"); - return -ENODEV; - } - - /* - * Get the base address for EHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the EHCI register address\n"); - return -ENXIO; - } - - exynos->hcd = (struct ehci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); - if (node <= 0) { - debug("EHCI: Can't get device node for usb-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb) { @@ -270,63 +211,6 @@ static void reset_usb_phy(struct exynos_usb_phy *usb) set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); } -#ifndef CONFIG_DM_USB -/* - * EHCI-initialization - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(int index, enum usb_init_type init, - struct ehci_hccr **hccr, struct ehci_hcor **hcor) -{ - struct exynos_ehci *ctx = &exynos; - -#ifdef CONFIG_OF_CONTROL - if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { - debug("Unable to parse device tree for ehci-exynos\n"); - return -ENODEV; - } -#else - ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); - ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); -#endif - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - setup_usb_phy(ctx->usb); - - board_usb_init(index, init); - - *hccr = ctx->hcd; - *hcor = (struct ehci_hcor *)((uint32_t) *hccr - + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the EHCI host controller. - */ -int ehci_hcd_stop(int index) -{ - struct exynos_ehci *ctx = &exynos; - - reset_usb_phy(ctx->usb); - - return 0; -} -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_probe(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -377,4 +261,3 @@ U_BOOT_DRIVER(usb_ehci) = { .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bd9861dd68..46d01d40e8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -125,14 +125,7 @@ static struct descriptor { static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev) { #ifdef CONFIG_DM_USB - struct udevice *dev; - - /* Find the USB controller */ - for (dev = udev->dev; - device_get_uclass_id(dev) != UCLASS_USB; - dev = dev->parent) - ; - return dev_get_priv(dev); + return dev_get_priv(usb_get_bus(udev->dev)); #else return udev->controller; #endif @@ -310,23 +303,33 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, * in the tree before that one! */ #ifdef CONFIG_DM_USB + /* + * When called from usb-uclass.c: usb_scan_device() udev->dev points + * to the parent udevice, not the actual udevice belonging to the + * udev as the device is not instantiated yet. So when searching + * for the first usb-2 parent start with udev->dev not + * udev->dev->parent . + */ struct udevice *parent; + struct usb_device *uparent; + + ttdev = udev; + parent = udev->dev; + uparent = dev_get_parentdata(parent); - for (ttdev = udev; ; ) { - struct udevice *dev = ttdev->dev; + while (uparent->speed != USB_SPEED_HIGH) { + struct udevice *dev = parent; - if (dev->parent && - device_get_uclass_id(dev->parent) == UCLASS_USB_HUB) - parent = dev->parent; - else - parent = NULL; - if (!parent) + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) { + printf("ehci: Error cannot find high speed parent of usb-1 device\n"); return; - ttdev = dev_get_parentdata(parent); - if (!ttdev->speed != USB_SPEED_HIGH) - break; + } + + ttdev = dev_get_parentdata(dev); + parent = dev->parent; + uparent = dev_get_parentdata(parent); } - parent_devnum = ttdev->devnum; + parent_devnum = uparent->devnum; #else ttdev = udev; while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) @@ -1576,12 +1579,15 @@ int ehci_register(struct udevice *dev, struct ehci_hccr *hccr, struct ehci_hcor *hcor, const struct ehci_ops *ops, uint tweaks, enum usb_init_type init) { + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); struct ehci_ctrl *ctrl = dev_get_priv(dev); int ret; debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__, dev->name, ctrl, hccr, hcor, init); + priv->desc_before_addr = true; + ehci_setup_ops(ctrl, ops); ctrl->hccr = hccr; ctrl->hcor = hcor; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 97a7edeb53..494b760761 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -103,16 +103,91 @@ static struct pci_device_id ehci_pci_ids[] = { # define m32_swap(x) cpu_to_le32(x) #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ +#ifdef CONFIG_DM_USB +/* + * We really should do proper cache flushing everywhere, but for now we only + * do it for new (driver-model) usb code to avoid regressions. + */ +#define flush_dcache_buffer(addr, size) \ + flush_dcache_range((unsigned long)(addr), \ + ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) +#define invalidate_dcache_buffer(addr, size) \ + invalidate_dcache_range((unsigned long)(addr), \ + ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) +#else +#define flush_dcache_buffer(addr, size) +#define invalidate_dcache_buffer(addr, size) +#endif + +/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */ +#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16) +#define flush_dcache_td(addr) flush_dcache_buffer(addr, 16) +#define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32) +#define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256) +#define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16) +#define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16) +#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32) +#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256) + /* global ohci_t */ static ohci_t gohci; /* this must be aligned to a 256 byte boundary */ struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* device which was disconnected */ -struct usb_device *devgone; + +/* mapping of the OHCI CC status to error codes */ +static int cc_to_error[16] = { + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +static const char *cc_to_string[16] = { + "No Error", + "CRC: Last data packet from endpoint contained a CRC error.", + "BITSTUFFING: Last data packet from endpoint contained a bit " \ + "stuffing violation", + "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ + "that did not match the expected value.", + "STALL: TD was moved to the Done Queue because the endpoint returned" \ + " a STALL PID", + "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ + "not provide a handshake (OUT)", + "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ + "(IN) or handshake (OUT)", + "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ + "value is not defined.", + "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ + "either the size of the maximum data packet allowed\n" \ + "from the endpoint (found in MaximumPacketSize field\n" \ + "of ED) or the remaining buffer size.", + "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ + "and that amount was not sufficient to fill the\n" \ + "specified buffer", + "reserved1", + "reserved2", + "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ + "than it could be written to system memory", + "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ + "system memory fast enough to keep up with data USB " \ + "data rate.", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(1)", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(2)", +}; static inline u32 roothub_a(struct ohci *hc) { return ohci_readl(&hc->regs->roothub.a); } @@ -124,11 +199,42 @@ static inline u32 roothub_portstatus(struct ohci *hc, int i) { return ohci_readl(&hc->regs->roothub.portstatus[i]); } /* forward declaration */ -static int hc_interrupt(void); -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, +static int hc_interrupt(ohci_t *ohci); +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval); +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, + unsigned long pipe, int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* TDs ... */ +static struct td *td_alloc(ohci_dev_t *ohci_dev, struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ohci_dev->tds[i].usb_dev == NULL) + { + td = &ohci_dev->tds[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} /*-------------------------------------------------------------------------* * URB support functions @@ -158,18 +264,18 @@ static void urb_free_priv(urb_priv_t *urb) /*-------------------------------------------------------------------------*/ #ifdef DEBUG -static int sohci_get_current_frame_number(struct usb_device *dev); +static int sohci_get_current_frame_number(ohci_t *ohci); /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void pkt_print(urb_priv_t *purb, struct usb_device *dev, +static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, char *str, int small) { dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", str, - sohci_get_current_frame_number(dev), + sohci_get_current_frame_number(ohci), usb_pipedevice(pipe), usb_pipeendpoint(pipe), usb_pipeout(pipe)? 'O': 'I', @@ -213,9 +319,11 @@ void ep_print_int_eds(ohci_t *ohci, char *str) ed_p = &(ohci->hcca->int_table [i]); if (*ed_p == 0) continue; + invalidate_dcache_ed(ed_p); printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); while (*ed_p != 0 && j--) { ed_t *ed = (ed_t *)m32_swap(ed_p); + invalidate_dcache_ed(ed); printf(" ed: %4x;", ed->hwINFO); ed_p = &ed->hwNextED; } @@ -246,6 +354,7 @@ static void maybe_print_eds(char *label, __u32 value) if (value) { dbg("%s %08x", label, value); + invalidate_dcache_ed(edp); dbg("%08x", edp->hwINFO); dbg("%08x", edp->hwTailP); dbg("%08x", edp->hwHeadP); @@ -380,6 +489,7 @@ static void ohci_dump(ohci_t *controller, int verbose) ohci_dump_status(controller); if (verbose) ep_print_int_eds(controller, "hcca"); + invalidate_dcache_hcca(controller->hcca); dbg("hcca frame #%04x", controller->hcca->frame_no); ohci_dump_roothub(controller, 1); } @@ -391,9 +501,9 @@ static void ohci_dump(ohci_t *controller, int verbose) /* get a transfer request */ -int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) +int sohci_submit_job(ohci_t *ohci, ohci_dev_t *ohci_dev, urb_priv_t *urb, + struct devrequest *setup) { - ohci_t *ohci; ed_t *ed; urb_priv_t *purb_priv = urb; int i, size = 0; @@ -403,8 +513,6 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) int transfer_len = urb->transfer_buffer_length; int interval = urb->interval; - ohci = &gohci; - /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { @@ -417,7 +525,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) urb->finished = 0; /* every endpoint has a ed, locate and fill it */ - ed = ep_add_ed(dev, pipe, interval, 1); + ed = ep_add_ed(ohci_dev, dev, pipe, interval, 1); if (!ed) { err("sohci_submit_job: ENOMEM"); return -1; @@ -453,7 +561,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) /* allocate the TDs */ /* note that td[0] was allocated in ep_add_ed */ for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc(dev); + purb_priv->td[i] = td_alloc(ohci_dev, dev); if (!purb_priv->td[i]) { purb_priv->length = i; urb_free_priv(purb_priv); @@ -473,7 +581,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) ep_link(ohci, ed); /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, + td_submit_job(ohci, dev, pipe, buffer, transfer_len, setup, purb_priv, interval); return 0; @@ -495,7 +603,7 @@ static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) ohci_readl(®s->intrdisable); /* PCI posting flush */ } urb->actual_length = 0; - td_submit_job( + td_submit_job( hc, urb->dev, urb->pipe, urb->transfer_buffer, @@ -517,11 +625,9 @@ static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) #ifdef DEBUG /* tell us the current USB frame number */ - -static int sohci_get_current_frame_number(struct usb_device *usb_dev) +static int sohci_get_current_frame_number(ohci_t *ohci) { - ohci_t *ohci = &gohci; - + invalidate_dcache_hcca(ohci->hcca); return m16_swap(ohci->hcca->frame_no); } #endif @@ -600,6 +706,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi) switch (ed->type) { case PIPE_CONTROL: ed->hwNextED = 0; + flush_dcache_ed(ed); if (ohci->ed_controltail == NULL) ohci_writel(ed, &ohci->regs->ed_controlhead); else @@ -617,6 +724,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi) case PIPE_BULK: ed->hwNextED = 0; + flush_dcache_ed(ed); if (ohci->ed_bulktail == NULL) ohci_writel(ed, &ohci->regs->ed_bulkhead); else @@ -649,7 +757,9 @@ static int ep_link(ohci_t *ohci, ed_t *edi) inter = ep_rev(6, ((ed_t *)ed_p)->int_interval); ed->hwNextED = *ed_p; + flush_dcache_ed(ed); *ed_p = m32_swap((unsigned long)ed); + flush_dcache_hcca(ohci->hcca); } break; } @@ -662,6 +772,8 @@ static int ep_link(ohci_t *ohci, ed_t *edi) static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, unsigned index, unsigned period) { + __maybe_unused unsigned long aligned_ed_p; + for (; index < NUM_INTS; index += period) { __u32 *ed_p = &ohci->hcca->int_table [index]; @@ -670,6 +782,12 @@ static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, if (((struct ed *) m32_swap((unsigned long)ed_p)) == ed) { *ed_p = ed->hwNextED; +#ifdef CONFIG_DM_USB + aligned_ed_p = (unsigned long)ed_p; + aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1); + flush_dcache_range(aligned_ed_p, + aligned_ed_p + ARCH_DMA_MINALIGN); +#endif break; } ed_p = &(((struct ed *) @@ -689,6 +807,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) int i; ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + flush_dcache_ed(ed); switch (ed->type) { case PIPE_CONTROL: @@ -702,6 +821,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) &ohci->regs->ed_controlhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; + flush_dcache_ed(ed->ed_prev); } if (ohci->ed_controltail == ed) { ohci->ed_controltail = ed->ed_prev; @@ -722,6 +842,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) &ohci->regs->ed_bulkhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; + flush_dcache_ed(ed->ed_prev); } if (ohci->ed_bulktail == ed) { ohci->ed_bulktail = ed->ed_prev; @@ -751,14 +872,14 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) * info fields are setted anyway even though most of them should not * change */ -static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, - int interval, int load) +static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, + unsigned long pipe, int interval, int load) { td_t *td; ed_t *ed_ret; volatile ed_t *ed; - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | + ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) | (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { @@ -769,12 +890,12 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, if (ed->state == ED_NEW) { /* dummy td; end of td list for ed */ - td = td_alloc(usb_dev); + td = td_alloc(ohci_dev, usb_dev); ed->hwTailP = m32_swap((unsigned long)td); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype(pipe); - ohci_dev.ed_cnt++; + ohci_dev->ed_cnt++; } ed->hwINFO = m32_swap(usb_pipedevice(pipe) @@ -790,6 +911,8 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, ed->int_load = load; } + flush_dcache_ed(ed); + return ed_ret; } @@ -815,6 +938,7 @@ static void td_fill(ohci_t *ohci, unsigned int info, /* use this td as the next dummy */ td_pt = urb_priv->td [index]; td_pt->hwNextTD = 0; + flush_dcache_td(td_pt); /* fill the old dummy TD */ td = urb_priv->td [index] = @@ -842,27 +966,30 @@ static void td_fill(ohci_t *ohci, unsigned int info, td->hwBE = 0; td->hwNextTD = m32_swap((unsigned long)td_pt); + flush_dcache_td(td); /* append to queue */ td->ed->hwTailP = td->hwNextTD; + flush_dcache_ed(td->ed); } /*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer */ -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) { - ohci_t *ohci = &gohci; int data_len = transfer_len; void *data; int cnt = 0; __u32 info = 0; unsigned int toggle = 0; + flush_dcache_buffer(buffer, data_len); + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle * bits for reseting */ if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { @@ -902,6 +1029,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe, case PIPE_CONTROL: /* Setup phase */ info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + flush_dcache_buffer(setup, 8); td_fill(ohci, info, setup, 8, dev, cnt++, urb); /* Optional Data phase */ @@ -914,7 +1042,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe, } /* Status phase */ - info = usb_pipeout(pipe)? + info = (usb_pipeout(pipe) || data_len == 0) ? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; td_fill(ohci, info, data, 0, dev, cnt++, urb); @@ -973,6 +1101,7 @@ static void check_status(td_t *td_list) if (cc) { err(" USB-error: %s (%x)", cc_to_string[cc], cc); + invalidate_dcache_ed(td_list->ed); if (*phwHeadP & m32_swap(0x1)) { if (lurb_priv && ((td_list->index + 1) < urb_len)) { @@ -985,9 +1114,11 @@ static void check_status(td_t *td_list) td_list->index - 1; } else *phwHeadP &= m32_swap(0xfffffff2); + flush_dcache_ed(td_list->ed); } #ifdef CONFIG_MPC5200 td_list->hwNextTD = 0; + flush_dcache_td(td_list); #endif } } @@ -1000,11 +1131,14 @@ static td_t *dl_reverse_done_list(ohci_t *ohci) td_t *td_rev = NULL; td_t *td_list = NULL; + invalidate_dcache_hcca(ohci->hcca); td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; ohci->hcca->done_head = 0; + flush_dcache_hcca(ohci->hcca); while (td_list_hc) { td_list = (td_t *)td_list_hc; + invalidate_dcache_td(td_list); check_status(td_list); td_list->next_dl_td = td_rev; td_rev = td_list; @@ -1039,6 +1173,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list) urb_priv_t *lurb_priv; __u32 tdINFO, edHeadP, edTailP; + invalidate_dcache_td(td_list); tdINFO = m32_swap(td_list->hwINFO); ed = td_list->ed; @@ -1064,6 +1199,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list) lurb_priv->td_cnt, lurb_priv->length); if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { + invalidate_dcache_ed(ed); edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap(ed->hwTailP); @@ -1100,16 +1236,16 @@ static int dl_done_list(ohci_t *ohci) #define OK(x) len = (x); break #ifdef DEBUG #define WR_RH_STAT(x) {info("WR:status %#8x", (x)); ohci_writel((x), \ - &gohci.regs->roothub.status); } + &ohci->regs->roothub.status); } #define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ - (x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } + (x)); ohci_writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); } #else -#define WR_RH_STAT(x) ohci_writel((x), &gohci.regs->roothub.status) +#define WR_RH_STAT(x) ohci_writel((x), &ohci->regs->roothub.status) #define WR_RH_PORTSTAT(x) ohci_writel((x), \ - &gohci.regs->roothub.portstatus[wIndex-1]) + &ohci->regs->roothub.portstatus[wIndex-1]) #endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) +#define RD_RH_STAT roothub_status(ohci) +#define RD_RH_PORTSTAT roothub_portstatus(ohci, wIndex-1) /* request to virtual root hub */ @@ -1137,8 +1273,9 @@ int rh_check_port_status(ohci_t *controller) return res; } -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) +static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *cmd) { void *data = buffer; int leni = transfer_len; @@ -1151,7 +1288,7 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); #ifdef DEBUG -pkt_print(NULL, dev, pipe, buffer, transfer_len, +pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); #else mdelay(1); @@ -1245,7 +1382,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, break; case RH_SET_ADDRESS: - gohci.rh.devnum = wValue; + ohci->rh.devnum = wValue; OK(0); case RH_GET_DESCRIPTOR: @@ -1290,7 +1427,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, case RH_GET_DESCRIPTOR | RH_CLASS: { - __u32 temp = roothub_a(&gohci); + __u32 temp = roothub_a(ohci); databuf[0] = 9; /* min length; */ databuf[1] = 0x29; @@ -1309,7 +1446,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, databuf[4] = 0; databuf[5] = (temp & RH_A_POTPGT) >> 24; databuf[6] = 0; - temp = roothub_b(&gohci); + temp = roothub_b(ohci); databuf[7] = temp & RH_B_DR; if (databuf[2] < 7) { databuf[8] = 0xff; @@ -1338,7 +1475,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, } #ifdef DEBUG - ohci_dump_roothub(&gohci, 1); + ohci_dump_roothub(ohci, 1); #else mdelay(1); #endif @@ -1350,7 +1487,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, dev->status = stat; #ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); #else mdelay(1); @@ -1363,8 +1500,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, /* common code for handling submit messages - used for all but root hub */ /* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) +static int submit_common_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup, int interval) { int stat = 0; int maxsize = usb_maxpacket(dev, pipe); @@ -1380,15 +1518,9 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, urb->transfer_buffer_length = transfer_len; urb->interval = interval; - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - #ifdef DEBUG urb->actual_length = 0; - pkt_print(urb, dev, pipe, buffer, transfer_len, + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else mdelay(1); @@ -1399,14 +1531,14 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - if (sohci_submit_job(urb, setup) < 0) { + if (sohci_submit_job(ohci, &ohci->ohci_dev, urb, setup) < 0) { err("sohci_submit_job failed"); return -1; } #if 0 mdelay(10); - /* ohci_dump_status(&gohci); */ + /* ohci_dump_status(ohci); */ #endif timeout = USB_TIMEOUT_MS(pipe); @@ -1414,7 +1546,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, /* wait for it to complete */ for (;;) { /* check whether the controller is done */ - stat = hc_interrupt(); + stat = hc_interrupt(ohci); if (stat < 0) { stat = USB_ST_CRC_ERR; break; @@ -1440,7 +1572,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dbg("*"); } else { - err("CTL:TIMEOUT "); + if (!usb_pipeint(pipe)) + err("CTL:TIMEOUT "); dbg("submit_common_msg: TO status %x\n", stat); urb->finished = 1; stat = USB_ST_CRC_ERR; @@ -1451,8 +1584,11 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dev->status = stat; dev->act_len = urb->actual_length; + if (usb_pipein(pipe) && dev->status == 0 && dev->act_len) + invalidate_dcache_buffer(buffer, dev->act_len); + #ifdef DEBUG - pkt_print(urb, dev, pipe, buffer, transfer_len, + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); #else mdelay(1); @@ -1469,17 +1605,27 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len) { info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); + return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, + NULL, 0); } -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL, + interval); +} + +static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup) { int maxsize = usb_maxpacket(dev, pipe); info("submit_control_msg"); #ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, transfer_len, + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else mdelay(1); @@ -1489,22 +1635,15 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, pipe); return -1; } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; + if (((pipe >> 8) & 0x7f) == ohci->rh.devnum) { + ohci->rh.dev = dev; /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); + return ohci_submit_rh_msg(ohci, dev, pipe, buffer, + transfer_len, setup); } - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, - interval); + return submit_common_msg(ohci, dev, pipe, buffer, transfer_len, + setup, 0); } /*-------------------------------------------------------------------------* @@ -1648,13 +1787,14 @@ static int hc_start(ohci_t *ohci) /* an interrupt happens */ -static int hc_interrupt(void) +static int hc_interrupt(ohci_t *ohci) { - ohci_t *ohci = &gohci; struct ohci_regs *regs = ohci->regs; int ints; int stat = -1; + invalidate_dcache_hcca(ohci->hcca); + if ((ohci->hcca->done_head != 0) && !(m32_swap(ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; @@ -1702,7 +1842,7 @@ static int hc_interrupt(void) mdelay(1); ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); (void)ohci_readl(®s->intrdisable); /* flush */ - stat = dl_done_list(&gohci); + stat = dl_done_list(ohci); ohci_writel(OHCI_INTR_WDH, ®s->intrenable); (void)ohci_readl(®s->intrdisable); /* flush */ } @@ -1772,21 +1912,9 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) err("HCCA not aligned!!"); return -1; } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset(phcca, 0, sizeof(struct ohci_hcca)); + gohci.hcca = &ghcca[0]; + info("aligned ghcca %p", gohci.hcca); + memset(gohci.hcca, 0, sizeof(struct ohci_hcca)); gohci.disabled = 1; gohci.sleeping = 0; @@ -1880,3 +2008,10 @@ int usb_lowlevel_stop(int index) ohci_inited = 0; return 0; } + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup) +{ + return _ohci_submit_control_msg(&gohci, dev, pipe, buffer, + transfer_len, setup); +} diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 9a4a2c2475..f52b4c1bb5 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -18,6 +18,18 @@ # define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) #endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ +#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 16 +#define ED_ALIGNMENT ARCH_DMA_MINALIGN +#else +#define ED_ALIGNMENT 16 +#endif + +#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 32 +#define TD_ALIGNMENT ARCH_DMA_MINALIGN +#else +#define TD_ALIGNMENT 32 +#endif + /* functions for doing board or CPU specific setup/cleanup */ int usb_board_stop(void); @@ -25,64 +37,7 @@ int usb_cpu_init(void); int usb_cpu_stop(void); int usb_cpu_init_fail(void); -static int cc_to_error[16] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ USB_ST_CRC_ERR, - /* Bit Stuff */ USB_ST_BIT_ERR, - /* Data Togg */ USB_ST_CRC_ERR, - /* Stall */ USB_ST_STALLED, - /* DevNotResp */ -1, - /* PIDCheck */ USB_ST_BIT_ERR, - /* UnExpPID */ USB_ST_BIT_ERR, - /* DataOver */ USB_ST_BUF_ERR, - /* DataUnder */ USB_ST_BUF_ERR, - /* reservd */ -1, - /* reservd */ -1, - /* BufferOver */ USB_ST_BUF_ERR, - /* BuffUnder */ USB_ST_BUF_ERR, - /* Not Access */ -1, - /* Not Access */ -1 -}; - -static const char *cc_to_string[16] = { - "No Error", - "CRC: Last data packet from endpoint contained a CRC error.", - "BITSTUFFING: Last data packet from endpoint contained a bit " \ - "stuffing violation", - "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ - "that did not match the expected value.", - "STALL: TD was moved to the Done Queue because the endpoint returned" \ - " a STALL PID", - "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ - "not provide a handshake (OUT)", - "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ - "(IN) or handshake (OUT)", - "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ - "value is not defined.", - "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ - "either the size of the maximum data packet allowed\n" \ - "from the endpoint (found in MaximumPacketSize field\n" \ - "of ED) or the remaining buffer size.", - "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ - "and that amount was not sufficient to fill the\n" \ - "specified buffer", - "reserved1", - "reserved2", - "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ - "than it could be written to system memory", - "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ - "system memory fast enough to keep up with data USB " \ - "data rate.", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(1)", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(2)", -}; - /* ED States */ - #define ED_NEW 0x00 #define ED_UNLINK 0x01 #define ED_OPER 0x02 @@ -109,7 +64,7 @@ struct ed { struct usb_device *usb_dev; void *purb; __u32 unused[2]; -} __attribute__((aligned(16))); +} __attribute__((aligned(ED_ALIGNMENT))); typedef struct ed ed_t; @@ -169,7 +124,7 @@ struct td { __u32 data; __u32 unused2[2]; -} __attribute__((aligned(32))); +} __attribute__((aligned(TD_ALIGNMENT))); typedef struct td td_t; #define OHCI_ED_SKIP (1 << 14) @@ -408,6 +363,16 @@ typedef struct } urb_priv_t; #define URB_DEL 1 +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +#define NUM_TD 64 /* we need more TDs than EDs */ + +typedef struct ohci_device { + ed_t ed[NUM_EDS] __aligned(ED_ALIGNMENT); + td_t tds[NUM_TD] __aligned(TD_ALIGNMENT); + int ed_cnt; +} ohci_dev_t; + /* * This is the full ohci controller description * @@ -417,6 +382,8 @@ typedef struct typedef struct ohci { + /* this allocates EDs for all possible endpoints */ + struct ohci_device ohci_dev __aligned(TD_ALIGNMENT); struct ohci_hcca *hcca; /* hcca */ /*dma_addr_t hcca_dma;*/ @@ -438,54 +405,3 @@ typedef struct ohci { const char *slot_name; } ohci_t; - -#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - int ed_cnt; -}; - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, - int interval, int load); - -/*-------------------------------------------------------------------------*/ - -/* we need more TDs than EDs */ -#define NUM_TD 64 - -/* +1 so we can align the storage */ -td_t gtd[NUM_TD+1]; -/* pointers to aligned storage */ -td_t *ptd; - -/* TDs ... */ -static inline struct td * -td_alloc (struct usb_device *usb_dev) -{ - int i; - struct td *td; - - td = NULL; - for (i = 0; i < NUM_TD; i++) - { - if (ptd[i].usb_dev == NULL) - { - td = &ptd[i]; - td->usb_dev = usb_dev; - break; - } - } - - return td; -} - -static inline void -ed_free (struct ed *ed) -{ - ed->usb_dev = NULL; -} diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 714bc0e958..c5ece58407 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -145,9 +145,8 @@ int usb_init(void) uclass_foreach_dev(bus, uc) { /* init low_level USB */ + printf("USB%d: ", count); count++; - printf("USB"); - printf("%d: ", bus->seq); ret = device_probe(bus); if (ret == -ENODEV) { /* No such device. */ puts("Port not available.\n"); @@ -477,9 +476,7 @@ int usb_scan_device(struct udevice *parent, int port, *devp = NULL; memset(udev, '\0', sizeof(*udev)); - ret = usb_get_bus(parent, &udev->controller_dev); - if (ret) - return ret; + udev->controller_dev = usb_get_bus(parent); priv = dev_get_uclass_priv(udev->controller_dev); /* @@ -536,11 +533,7 @@ int usb_scan_device(struct udevice *parent, int port, plat = dev_get_parent_platdata(dev); debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat); plat->devnum = udev->devnum; - plat->speed = udev->speed; - plat->slot_id = udev->slot_id; - plat->portnr = port; - debug("** device '%s': stashing slot_id=%d\n", dev->name, - plat->slot_id); + plat->udev = udev; priv->next_addr++; ret = device_probe(dev); if (ret) { @@ -579,45 +572,55 @@ int usb_child_post_bind(struct udevice *dev) return 0; } -int usb_get_bus(struct udevice *dev, struct udevice **busp) +struct udevice *usb_get_bus(struct udevice *dev) { struct udevice *bus; - *busp = NULL; for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; ) bus = bus->parent; if (!bus) { /* By design this cannot happen */ assert(bus); debug("USB HUB '%s' does not have a controller\n", dev->name); - return -EXDEV; } - *busp = bus; - return 0; + return bus; } int usb_child_pre_probe(struct udevice *dev) { - struct udevice *bus; struct usb_device *udev = dev_get_parentdata(dev); struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); int ret; - ret = usb_get_bus(dev, &bus); - if (ret) - return ret; - udev->controller_dev = bus; - udev->dev = dev; - udev->devnum = plat->devnum; - udev->slot_id = plat->slot_id; - udev->portnr = plat->portnr; - udev->speed = plat->speed; - debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id); - - ret = usb_select_config(udev); - if (ret) - return ret; + if (plat->udev) { + /* + * Copy over all the values set in the on stack struct + * usb_device in usb_scan_device() to our final struct + * usb_device for this dev. + */ + *udev = *(plat->udev); + /* And clear plat->udev as it will not be valid for long */ + plat->udev = NULL; + udev->dev = dev; + } else { + /* + * This happens with devices which are explicitly bound + * instead of being discovered through usb_scan_device() + * such as sandbox emul devices. + */ + udev->dev = dev; + udev->controller_dev = usb_get_bus(dev); + udev->devnum = plat->devnum; + + /* + * udev did not go through usb_scan_device(), so we need to + * select the config and read the config descriptors. + */ + ret = usb_select_config(udev); + if (ret) + return ret; + } return 0; } diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index 23c7ecc5d8..a27a79632b 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -33,36 +33,24 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_USB struct exynos_xhci_platdata { fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif /** * Contains pointers to register base addresses * for the usb controller. */ struct exynos_xhci { -#ifdef CONFIG_DM_USB struct usb_platdata usb_plat; -#endif struct xhci_ctrl ctrl; struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif }; -#ifndef CONFIG_DM_USB -static struct exynos_xhci exynos; -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -102,54 +90,6 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev) return 0; } -#else -static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI); - if (node <= 0) { - debug("XHCI: Can't get device node for xhci\n"); - return -ENODEV; - } - - /* - * Get the base address for XHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the XHCI register base address\n"); - return -ENXIO; - } - exynos->hcd = (struct xhci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); - if (node <= 0) { - debug("XHCI: Can't get device node for usb3-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb3_phy == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) { @@ -340,53 +280,6 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos) exynos5_usb3_phy_exit(exynos->usb3_phy); } -#ifndef CONFIG_DM_USB -int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) -{ - struct exynos_xhci *ctx = &exynos; - int ret; - -#ifdef CONFIG_OF_CONTROL - exynos_usb3_parse_dt(gd->fdt_blob, ctx); -#else - ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy(); - ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci(); -#endif - - ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - ret = exynos_xhci_core_init(ctx); - if (ret) { - puts("XHCI: failed to initialize controller\n"); - return -EINVAL; - } - - *hccr = (ctx->hcd); - *hcor = (struct xhci_hcor *)((uint32_t) *hccr - + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -void xhci_hcd_stop(int index) -{ - struct exynos_xhci *ctx = &exynos; - - exynos_xhci_core_exit(ctx); -} -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_probe(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -443,4 +336,3 @@ U_BOOT_DRIVER(usb_xhci) = { .priv_auto_alloc_size = sizeof(struct exynos_xhci), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/include/bcd.h b/include/bcd.h index af4aa9c7ba..9ecd328284 100644 --- a/include/bcd.h +++ b/include/bcd.h @@ -10,14 +10,12 @@ #ifndef _BCD_H #define _BCD_H -#include <linux/types.h> - -static inline unsigned int bcd2bin(u8 val) +static inline unsigned int bcd2bin(unsigned int val) { - return ((val) & 0x0f) + ((val) >> 4) * 10; + return ((val) & 0x0f) + ((val & 0xff) >> 4) * 10; } -static inline u8 bin2bcd (unsigned int val) +static inline unsigned int bin2bcd(unsigned int val) { return (((val / 10) << 4) | (val % 10)); } diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 3bf45a224d..f5361d1091 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -125,6 +125,8 @@ func(HOST, host, 1) \ func(HOST, host, 0) +#define CONFIG_BOOTCOMMAND "" + #include <config_distro_bootcmd.h> #define CONFIG_KEEP_SERVERADDR @@ -207,5 +209,6 @@ #define CONFIG_CMD_LZMADEC #define CONFIG_CMD_USB +#define CONFIG_CMD_DATE #endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 395e25a431..08f1bad938 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -46,6 +46,7 @@ enum uclass_id { UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_CPU, /* CPU, typically part of an SoC */ + UCLASS_RTC, /* Real time clock device */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/fdtdec.h b/include/fdtdec.h index 659047097a..f11475b5fd 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -145,8 +145,6 @@ enum fdt_compat_id { COMPAT_SAMSUNG_EXYNOS5_SOUND, /* Exynos Sound */ COMPAT_WOLFSON_WM8994_CODEC, /* Wolfson WM8994 Sound Codec */ COMPAT_GOOGLE_CROS_EC_KEYB, /* Google CROS_EC Keyboard */ - COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */ - COMPAT_SAMSUNG_EXYNOS5_XHCI, /* Exynos5 XHCI controller */ COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */ COMPAT_SAMSUNG_EXYNOS5_USB3_PHY,/* Exynos phy controller for usb3.0 */ COMPAT_SAMSUNG_EXYNOS_TMU, /* Exynos TMU */ diff --git a/include/i2c.h b/include/i2c.h index 6fd73fae4c..1e259861b7 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -54,6 +54,7 @@ struct dm_i2c_chip { uint flags; #ifdef CONFIG_SANDBOX struct udevice *emul; + bool test_mode; #endif }; @@ -124,6 +125,27 @@ int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, struct udevice **devp); /** + * dm_i2c_reg_read() - Read a value from an I2C register + * + * This reads a single value from the given address in an I2C chip + * + * @addr: Address to read from + * @return value read, or -ve on error + */ +int dm_i2c_reg_read(struct udevice *dev, uint offset); + +/** + * dm_i2c_reg_write() - Write a value to an I2C register + * + * This writes a single value to the given address in an I2C chip + * + * @addr: Address to write to + * @val: Value to write (normally a byte) + * @return 0 on success, -ve on error + */ +int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val); + +/** * dm_i2c_set_bus_speed() - set the speed of a bus * * @bus: Bus to adjust diff --git a/include/os.h b/include/os.h index a758f099aa..ffbdce8464 100644 --- a/include/os.h +++ b/include/os.h @@ -13,6 +13,7 @@ #include <linux/types.h> +struct rtc_time; struct sandbox_state; /** @@ -277,4 +278,14 @@ int os_read_ram_buf(const char *fname); */ int os_jump_to_image(const void *dest, int size); +/** + * Read the current system time + * + * This reads the current Local Time and places it into the provided + * structure. + * + * @param rt Place to put system time + */ +void os_localtime(struct rtc_time *rt); + #endif diff --git a/include/rtc.h b/include/rtc.h index 54e361ea5e..bd8621d60b 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -15,41 +15,143 @@ * it there instead of in evey single driver */ #include <bcd.h> +#include <rtc_def.h> -/* - * The struct used to pass data from the generic interface code to - * the hardware dependend low-level code ande vice versa. Identical - * to struct rtc_time used by the Linux kernel. - * - * Note that there are small but significant differences to the - * common "struct time": - * - * struct time: struct rtc_time: - * tm_mon 0 ... 11 1 ... 12 - * tm_year years since 1900 years since 0 - */ - -struct rtc_time { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; +#ifdef CONFIG_DM_RTC + +struct rtc_ops { + /** + * get() - get the current time + * + * Returns the current time read from the RTC device. The driver + * is responsible for setting up every field in the structure. + * + * @dev: Device to read from + * @time: Place to put the time that is read + */ + int (*get)(struct udevice *dev, struct rtc_time *time); + + /** + * set() - set the current time + * + * Sets the time in the RTC device. The driver can expect every + * field to be set correctly. + * + * @dev: Device to read from + * @time: Time to write + */ + int (*set)(struct udevice *dev, const struct rtc_time *time); + + /** + * reset() - reset the RTC to a known-good state + * + * This function resets the RTC to a known-good state. The time may + * be unset by this method, so should be set after this method is + * called. + * + * @dev: Device to read from + * @return 0 if OK, -ve on error + */ + int (*reset)(struct udevice *dev); + + /** + * read8() - Read an 8-bit register + * + * @dev: Device to read from + * @reg: Register to read + * @return value read, or -ve on error + */ + int (*read8)(struct udevice *dev, unsigned int reg); + + /** + * write8() - Write an 8-bit register + * + * @dev: Device to write to + * @reg: Register to write + * @value: Value to write + * @return 0 if OK, -ve on error + */ + int (*write8)(struct udevice *dev, unsigned int reg, int val); }; +/* Access the operations for an RTC device */ +#define rtc_get_ops(dev) ((struct rtc_ops *)(dev)->driver->ops) + +/** + * dm_rtc_get() - Read the time from an RTC + * + * @dev: Device to read from + * @time: Place to put the current time + * @return 0 if OK, -ve on error + */ +int dm_rtc_get(struct udevice *dev, struct rtc_time *time); + +/** + * dm_rtc_put() - Write a time to an RTC + * + * @dev: Device to read from + * @time: Time to write into the RTC + * @return 0 if OK, -ve on error + */ +int dm_rtc_set(struct udevice *dev, struct rtc_time *time); + +/** + * dm_rtc_reset() - reset the RTC to a known-good state + * + * If the RTC appears to be broken (e.g. it is not counting up in seconds) + * it may need to be reset to a known good state. This function achieves this. + * After resetting the RTC the time should then be set to a known value by + * the caller. + * + * @dev: Device to read from + * @return 0 if OK, -ve on error + */ +int dm_rtc_reset(struct udevice *dev); + +/** + * rtc_read8() - Read an 8-bit register + * + * @dev: Device to read from + * @reg: Register to read + * @return value read, or -ve on error + */ +int rtc_read8(struct udevice *dev, unsigned int reg); + +/** + * rtc_write8() - Write an 8-bit register + * + * @dev: Device to write to + * @reg: Register to write + * @value: Value to write + * @return 0 if OK, -ve on error + */ +int rtc_write8(struct udevice *dev, unsigned int reg, int val); + +/** + * rtc_read32() - Read a 32-bit value from the RTC + * + * @dev: Device to read from + * @reg: Offset to start reading from + * @valuep: Place to put the value that is read + * @return 0 if OK, -ve on error + */ +int rtc_read32(struct udevice *dev, unsigned int reg, u32 *valuep); + +/** + * rtc_write32() - Write a 32-bit value to the RTC + * + * @dev: Device to write to + * @reg: Register to start writing to + * @value: Value to write + * @return 0 if OK, -ve on error + */ +int rtc_write32(struct udevice *dev, unsigned int reg, u32 value); + +#else int rtc_get (struct rtc_time *); int rtc_set (struct rtc_time *); void rtc_reset (void); -void GregorianDay (struct rtc_time *); -void to_tm (int, struct rtc_time *); -unsigned long mktime (unsigned int, unsigned int, unsigned int, - unsigned int, unsigned int, unsigned int); - /** * rtc_read8() - Read an 8-bit register * @@ -86,5 +188,44 @@ void rtc_write32(int reg, u32 value); * rtc_init() - Set up the real time clock ready for use */ void rtc_init(void); +#endif + +/** + * rtc_calc_weekday() - Work out the weekday from a time + * + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK). + * It sets time->tm_wdaay to the correct day of the week. + * + * @time: Time to inspect. tm_wday is updated + * @return 0 if OK, -EINVAL if the weekday could not be determined + */ +int rtc_calc_weekday(struct rtc_time *time); + +/** + * rtc_to_tm() - Convert a time_t value into a broken-out time + * + * The following fields are set up by this function: + * tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday + * + * Note that tm_yday and tm_isdst are set to 0. + * + * @time_t: Number of seconds since 1970-01-01 00:00:00 + * @time: Place to put the broken-out time + * @return 0 if OK, -EINVAL if the weekday could not be determined + */ +int rtc_to_tm(int time_t, struct rtc_time *time); + +/** + * rtc_mktime() - Convert a broken-out time into a time_t value + * + * The following fields need to be valid for this function to work: + * tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year + * + * Note that tm_wday and tm_yday are ignored. + * + * @time: Broken-out time to convert + * @return corresponding time_t value, seconds since 1970-01-01 00:00:00 + */ +unsigned long rtc_mktime(const struct rtc_time *time); #endif /* _RTC_H_ */ diff --git a/include/rtc_def.h b/include/rtc_def.h new file mode 100644 index 0000000000..6179797267 --- /dev/null +++ b/include/rtc_def.h @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __rtc_def_h +#define __rtc_def_h + +/* + * The struct used to pass data from the generic interface code to + * the hardware dependend low-level code ande vice versa. Identical + * to struct rtc_time used by the Linux kernel. + * + * Note that there are small but significant differences to the + * common "struct time": + * + * struct time: struct rtc_time: + * tm_mon 0 ... 11 1 ... 12 + * tm_year years since 1900 years since 0 + */ + +struct rtc_time { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#endif diff --git a/include/spi.h b/include/spi.h index 9495ca53c9..f4b93e6a13 100644 --- a/include/spi.h +++ b/include/spi.h @@ -100,6 +100,8 @@ struct dm_spi_slave_platdata { * @dev: SPI slave device * @max_hz: Maximum speed for this slave * @mode: SPI mode to use for this slave (see SPI mode flags) + * @speed: Current bus speed. This is 0 until the bus is first + * claimed. * @bus: ID of the bus that the slave is attached to. For * driver model this is the sequence number of the SPI * bus (bus->seq) so does not need to be stored @@ -117,6 +119,7 @@ struct spi_slave { #ifdef CONFIG_DM_SPI struct udevice *dev; /* struct spi_slave is dev->parentdata */ uint max_hz; + uint speed; uint mode; #else unsigned int bus; @@ -613,7 +616,7 @@ int sandbox_spi_get_emul(struct sandbox_state *state, struct udevice *bus, struct udevice *slave, struct udevice **emulp); -/* Access the serial operations for a device */ +/* Access the operations for a SPI device */ #define spi_get_ops(dev) ((struct dm_spi_ops *)(dev)->driver->ops) #define spi_emul_get_ops(dev) ((struct dm_spi_emul_ops *)(dev)->driver->ops) #endif /* CONFIG_DM_SPI */ diff --git a/include/usb.h b/include/usb.h index 1984e8f590..4c21050203 100644 --- a/include/usb.h +++ b/include/usb.h @@ -571,20 +571,23 @@ struct usb_platdata { * This is used by sandbox to provide emulation data also. * * @id: ID used to match this device - * @speed: Stores the speed associated with a USB device * @devnum: Device address on the USB bus - * @slot_id: USB3 slot ID, which is separate from the device address - * @portnr: Port number of this device on its parent hub, numbered from 1 - * (0 mean this device is the root hub) + * @udev: usb-uclass internal use only do NOT use * @strings: List of descriptor strings (for sandbox emulation purposes) * @desc_list: List of descriptors (for sandbox emulation purposes) */ struct usb_dev_platdata { struct usb_device_id id; - enum usb_device_speed speed; int devnum; - int slot_id; - int portnr; /* Hub port number, 1..n */ + /* + * This pointer is used to pass the usb_device used in usb_scan_device, + * to get the usb descriptors before the driver is known, to the + * actual udevice once the driver is known and the udevice is created. + * This will be NULL except during probe, do NOT use. + * + * This should eventually go away. + */ + struct usb_device *udev; #ifdef CONFIG_SANDBOX struct usb_string *strings; /* NULL-terminated list of descriptor pointers */ @@ -742,11 +745,10 @@ int usb_scan_device(struct udevice *parent, int port, * will be a device with uclass UCLASS_USB. * * @dev: Device to check - * @busp: Returns bus, or NULL if not found - * @return 0 if OK, -EXDEV is somehow this bus does not have a controller (this - * indicates a critical error in the USB stack + * @return The bus, or NULL if not found (this indicates a critical error in + * the USB stack */ -int usb_get_bus(struct udevice *dev, struct udevice **busp); +struct udevice *usb_get_bus(struct udevice *dev); /** * usb_select_config() - Set up a device ready for use diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 80b897a21c..b76d9cad83 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -44,8 +44,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SAMSUNG_EXYNOS5_SOUND, "samsung,exynos-sound"), COMPAT(WOLFSON_WM8994_CODEC, "wolfson,wm8994-codec"), COMPAT(GOOGLE_CROS_EC_KEYB, "google,cros-ec-keyb"), - COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"), - COMPAT(SAMSUNG_EXYNOS5_XHCI, "samsung,exynos5250-xhci"), COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"), COMPAT(SAMSUNG_EXYNOS5_USB3_PHY, "samsung,exynos5250-usb3-phy"), COMPAT(SAMSUNG_EXYNOS_TMU, "samsung,exynos-tmu"), diff --git a/net/sntp.c b/net/sntp.c index 6422eef72e..9c8ee34a4e 100644 --- a/net/sntp.c +++ b/net/sntp.c @@ -7,6 +7,7 @@ #include <common.h> #include <command.h> +#include <dm.h> #include <net.h> #include <rtc.h> @@ -68,9 +69,20 @@ static void sntp_handler(uchar *pkt, unsigned dest, struct in_addr sip, */ memcpy(&seconds, &rpktp->transmit_timestamp, sizeof(ulong)); - to_tm(ntohl(seconds) - 2208988800UL + net_ntp_time_offset, &tm); + rtc_to_tm(ntohl(seconds) - 2208988800UL + net_ntp_time_offset, &tm); #if defined(CONFIG_CMD_DATE) +# ifdef CONFIG_DM_RTC + struct udevice *dev; + int ret; + + ret = uclass_get_device(UCLASS_RTC, 0, &dev); + if (ret) + printf("SNTP: cannot find RTC: err=%d\n", ret); + else + dm_rtc_set(dev, &tm); +# else rtc_set(&tm); +# endif #endif printf("Date: %4d-%02d-%02d Time: %2d:%02d:%02d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, diff --git a/post/drivers/rtc.c b/post/drivers/rtc.c index cd19f7568d..c2e73917ec 100644 --- a/post/drivers/rtc.c +++ b/post/drivers/rtc.c @@ -59,11 +59,10 @@ static int rtc_post_skip (ulong * diff) static void rtc_post_restore (struct rtc_time *tm, unsigned int sec) { - time_t t = mktime (tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, - tm->tm_min, tm->tm_sec) + sec; + time_t t = rtc_mktime(tm) + sec; struct rtc_time ntm; - to_tm (t, &ntm); + rtc_to_tm(t, &ntm); rtc_set (&ntm); } @@ -116,10 +115,17 @@ int rtc_post_test (int flags) rtc_get (&svtm); for (i = 0; i < 12; i++) { - time_t t = mktime (ynl, i + 1, daysnl[i], 23, 59, 59); + time_t t; struct rtc_time tm; - to_tm (t, &tm); + tm.tm_year = ynl; + tm.tm_mon = i + 1; + tm.tm_mday = daysnl[i]; + tm.tm_hour = 23; + tm.tm_min = 59; + tm.tm_sec = 59; + t = rtc_mktime(&tm); + rtc_to_tm(t, &tm); rtc_set (&tm); skipped++; @@ -140,10 +146,18 @@ int rtc_post_test (int flags) } for (i = 0; i < 12; i++) { - time_t t = mktime (yl, i + 1, daysl[i], 23, 59, 59); + time_t t; struct rtc_time tm; - to_tm (t, &tm); + tm.tm_year = yl; + tm.tm_mon = i + 1; + tm.tm_mday = daysl[i]; + tm.tm_hour = 23; + tm.tm_min = 59; + tm.tm_sec = 59; + t = rtc_mktime(&tm); + + rtc_to_tm(t, &tm); rtc_set (&tm); skipped++; diff --git a/test/Kconfig b/test/Kconfig index 1fb1716a4a..3270c84213 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -1 +1,9 @@ +config CMD_UT_TIME + bool "Unit tests for time functions" + help + Enables the 'ut_time' command which tests that the time functions + work correctly. The test is fairly simple and will not catch all + problems. But if you are having problems with udelay() and the like, + this is a good place to start. + source "test/dm/Kconfig" diff --git a/test/Makefile b/test/Makefile index 9c95805c44..08330e020c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_SANDBOX) += command_ut.o obj-$(CONFIG_SANDBOX) += compression.o +obj-$(CONFIG_CMD_UT_TIME) += time_ut.o diff --git a/test/dm/Makefile b/test/dm/Makefile index fd9e29f201..a0cc2c6de5 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_DM_ETH) += eth.o obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_I2C) += i2c.o obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_DM_RTC) += rtc.o obj-$(CONFIG_DM_SPI_FLASH) += sf.o obj-$(CONFIG_DM_SPI) += spi.o obj-$(CONFIG_DM_USB) += usb.o diff --git a/test/dm/eth.c b/test/dm/eth.c index 4891f3ad34..196eba85a2 100644 --- a/test/dm/eth.c +++ b/test/dm/eth.c @@ -135,6 +135,7 @@ static int dm_test_net_retry(struct dm_test_state *dms) sandbox_eth_disable_response(1, true); setenv("ethact", "eth@10004000"); setenv("netretry", "yes"); + sandbox_eth_skip_timeout(); ut_assertok(net_loop(PING)); ut_asserteq_str("eth@10002000", getenv("ethact")); @@ -144,6 +145,7 @@ static int dm_test_net_retry(struct dm_test_state *dms) */ setenv("ethact", "eth@10004000"); setenv("netretry", "no"); + sandbox_eth_skip_timeout(); ut_asserteq(-ETIMEDOUT, net_loop(PING)); ut_asserteq_str("eth@10004000", getenv("ethact")); diff --git a/test/dm/i2c.c b/test/dm/i2c.c index 541b73b803..c5939a165e 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -66,6 +66,9 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) uint8_t buf[5]; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + + /* Use test mode so we create the required errors for invalid speeds */ + sandbox_i2c_set_test_mode(bus, true); ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); ut_assertok(dm_i2c_set_bus_speed(bus, 100000)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); @@ -73,6 +76,7 @@ static int dm_test_i2c_speed(struct dm_test_state *dms) ut_asserteq(400000, dm_i2c_get_bus_speed(bus)); ut_assertok(dm_i2c_read(dev, 0, buf, 5)); ut_asserteq(-EINVAL, dm_i2c_write(dev, 0, buf, 5)); + sandbox_i2c_set_test_mode(bus, false); return 0; } @@ -100,7 +104,11 @@ static int dm_test_i2c_probe_empty(struct dm_test_state *dms) struct udevice *bus, *dev; ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + + /* Use test mode so that this chip address will always probe */ + sandbox_i2c_set_test_mode(bus, true); ut_assertok(dm_i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); + sandbox_i2c_set_test_mode(bus, false); return 0; } diff --git a/test/dm/rtc.c b/test/dm/rtc.c new file mode 100644 index 0000000000..9397cf72a7 --- /dev/null +++ b/test/dm/rtc.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <rtc.h> +#include <asm/io.h> +#include <dm/test.h> +#include <dm/ut.h> +#include <asm/test.h> + +/* Simple RTC sanity check */ +static int dm_test_rtc_base(struct dm_test_state *dms) +{ + struct udevice *dev; + + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_RTC, 2, &dev)); + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev)); + + return 0; +} +DM_TEST(dm_test_rtc_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static void show_time(const char *msg, struct rtc_time *time) +{ + printf("%s: %02d/%02d/%04d %02d:%02d:%02d\n", msg, + time->tm_mday, time->tm_mon, time->tm_year, + time->tm_hour, time->tm_min, time->tm_sec); +} + +static int cmp_times(struct rtc_time *expect, struct rtc_time *time, bool show) +{ + bool same; + + same = expect->tm_sec == time->tm_sec; + same &= expect->tm_min == time->tm_min; + same &= expect->tm_hour == time->tm_hour; + same &= expect->tm_mday == time->tm_mday; + same &= expect->tm_mon == time->tm_mon; + same &= expect->tm_year == time->tm_year; + if (!same && show) { + show_time("expected", expect); + show_time("actual", time); + } + + return same ? 0 : -EINVAL; +} + +/* Set and get the time */ +static int dm_test_rtc_set_get(struct dm_test_state *dms) +{ + struct rtc_time now, time, cmp; + struct udevice *dev, *emul; + long offset, old_offset, old_base_time; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(dm_rtc_get(dev, &now)); + + ut_assertok(device_find_first_child(dev, &emul)); + ut_assert(emul != NULL); + + /* Tell the RTC to go into manual mode */ + old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0); + old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1); + + memset(&time, '\0', sizeof(time)); + time.tm_mday = 25; + time.tm_mon = 8; + time.tm_year = 2004; + time.tm_sec = 0; + time.tm_min = 18; + time.tm_hour = 18; + ut_assertok(dm_rtc_set(dev, &time)); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&time, &cmp, true)); + + /* Increment by 1 second */ + offset = sandbox_i2c_rtc_set_offset(emul, false, 0); + sandbox_i2c_rtc_set_offset(emul, false, offset + 1); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_asserteq(1, cmp.tm_sec); + + /* Check against original offset */ + sandbox_i2c_rtc_set_offset(emul, false, old_offset); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&now, &cmp, true)); + + /* Back to the original offset */ + sandbox_i2c_rtc_set_offset(emul, false, 0); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&now, &cmp, true)); + + /* Increment the base time by 1 emul */ + sandbox_i2c_rtc_get_set_base_time(emul, old_base_time + 1); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + if (now.tm_sec == 59) { + ut_asserteq(0, cmp.tm_sec); + } else { + ut_asserteq(now.tm_sec + 1, cmp.tm_sec); + } + + old_offset = sandbox_i2c_rtc_set_offset(emul, true, 0); + + return 0; +} +DM_TEST(dm_test_rtc_set_get, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Reset the time */ +static int dm_test_rtc_reset(struct dm_test_state *dms) +{ + struct rtc_time now; + struct udevice *dev, *emul; + long old_base_time, base_time; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(dm_rtc_get(dev, &now)); + + ut_assertok(device_find_first_child(dev, &emul)); + ut_assert(emul != NULL); + + old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, 0); + + ut_asserteq(0, sandbox_i2c_rtc_get_set_base_time(emul, -1)); + + /* Resetting the RTC should put he base time back to normal */ + ut_assertok(dm_rtc_reset(dev)); + base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1); + ut_asserteq(old_base_time, base_time); + + return 0; +} +DM_TEST(dm_test_rtc_reset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Check that two RTC devices can be used independently */ +static int dm_test_rtc_dual(struct dm_test_state *dms) +{ + struct rtc_time now1, now2, cmp; + struct udevice *dev1, *dev2; + struct udevice *emul1, *emul2; + long offset; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev1)); + ut_assertok(dm_rtc_get(dev1, &now1)); + ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev2)); + ut_assertok(dm_rtc_get(dev2, &now2)); + + ut_assertok(device_find_first_child(dev1, &emul1)); + ut_assert(emul1 != NULL); + ut_assertok(device_find_first_child(dev2, &emul2)); + ut_assert(emul2 != NULL); + + offset = sandbox_i2c_rtc_set_offset(emul1, false, -1); + sandbox_i2c_rtc_set_offset(emul2, false, offset + 1); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev2, &cmp)); + ut_asserteq(-EINVAL, cmp_times(&now1, &cmp, false)); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev1, &cmp)); + ut_assertok(cmp_times(&now1, &cmp, true)); + + return 0; +} +DM_TEST(dm_test_rtc_dual, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test.dts b/test/dm/test.dts index d0c40be6b0..008584865c 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -8,18 +8,20 @@ aliases { console = &uart0; + eth0 = "/eth@10002000"; + eth5 = ð_5; i2c0 = "/i2c@0"; - spi0 = "/spi@0"; pci0 = &pci; - testfdt6 = "/e-test"; + spi0 = "/spi@0"; testbus3 = "/some-bus"; testfdt0 = "/some-bus/c-test@0"; testfdt1 = "/some-bus/c-test@1"; testfdt3 = "/b-test"; testfdt5 = "/some-bus/c-test@5"; + testfdt6 = "/e-test"; testfdt8 = "/a-test"; - eth0 = "/eth@10002000"; - eth5 = ð_5; + rtc0 = &rtc_0; + rtc1 = &rtc_1; usb0 = &usb_0; usb1 = &usb_1; usb2 = &usb_2; @@ -139,6 +141,22 @@ sandbox,size = <256>; }; }; + + rtc_0: rtc@43 { + reg = <0x43>; + compatible = "sandbox-rtc"; + emul { + compatible = "sandbox,i2c-rtc"; + }; + }; + + rtc_1: rtc@61 { + reg = <0x61>; + compatible = "sandbox-rtc"; + emul { + compatible = "sandbox,i2c-rtc"; + }; + }; }; pci: pci-controller { diff --git a/test/time_ut.c b/test/time_ut.c new file mode 100644 index 0000000000..6b52245d7f --- /dev/null +++ b/test/time_ut.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <errno.h> + +static int test_get_timer(void) +{ + ulong base, start, next, diff; + int iter; + + base = get_timer(0); + start = get_timer(0); + for (iter = 0; iter < 10; iter++) { + do { + next = get_timer(0); + } while (start == next); + + if (start + 1 != next) { + printf("%s: iter=%d, start=%lu, next=%lu, expected a difference of 1\n", + __func__, iter, start, next); + return -EINVAL; + } + start++; + } + + /* + * Check that get_timer(base) matches our elapsed time, allowing that + * an extra millisecond may have passed. + */ + diff = get_timer(base); + if (diff != iter && diff != iter + 1) { + printf("%s: expected get_timer(base) to match elapsed time: diff=%lu, expected=%d\n", + __func__, diff, iter); + return -EINVAL; + } + + return 0; +} + +static int test_timer_get_us(void) +{ + ulong prev, next, min = 1000000; + long delta; + int iter; + + /* Find the minimum delta we can measure, in microseconds */ + prev = timer_get_us(); + for (iter = 0; iter < 100; ) { + next = timer_get_us(); + if (next != prev) { + delta = next - prev; + if (delta < 0) { + printf("%s: timer_get_us() went backwards from %lu to %lu\n", + __func__, prev, next); + return -EINVAL; + } else if (delta != 0) { + if (delta < min) + min = delta; + prev = next; + iter++; + } + } + } + + if (min != 1) { + printf("%s: Minimum microsecond delta should be 1 but is %lu\n", + __func__, min); + return -EINVAL; + } + + return 0; +} + +static int test_time_comparison(void) +{ + ulong start_us, end_us, delta_us; + long error; + ulong start; + + start = get_timer(0); + start_us = timer_get_us(); + while (get_timer(start) < 1000) + ; + end_us = timer_get_us(); + delta_us = end_us - start_us; + error = delta_us - 1000000; + printf("%s: Microsecond time for 1 second: %lu, error = %ld\n", + __func__, delta_us, error); + if (abs(error) > 1000) + return -EINVAL; + + return 0; +} + +static int test_udelay(void) +{ + long error; + ulong start, delta; + int iter; + + start = get_timer(0); + for (iter = 0; iter < 1000; iter++) + udelay(1000); + delta = get_timer(start); + error = delta - 1000; + printf("%s: Delay time for 1000 udelay(1000): %lu ms, error = %ld\n", + __func__, delta, error); + if (abs(error) > 100) + return -EINVAL; + + return 0; +} + +static int do_ut_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = 0; + + ret |= test_get_timer(); + ret |= test_timer_get_us(); + ret |= test_time_comparison(); + ret |= test_udelay(); + + printf("Test %s\n", ret ? "failed" : "passed"); + + return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + ut_time, 1, 1, do_ut_time, + "Very basic test of time functions", + "" +); |