From b6ae6765c5a9e5daa3799e4d65562d3184712506 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 9 Jun 2014 11:36:55 +0200 Subject: sunxi: Remove mmc DMA support The DMA code in sunxi_mmc.c is broken. mmc_trans_data_by_dma() allocates the dma descriptors on the stack, and then exits while the dma transfer is in progress, so the dma engine is reading stack memory which at that point may be re-used. So far we've gotten away with this by luck, but recent u-boot changes have shifted the stack start address by 16 bytes, which combined with dma alignment now exposes this problem. Since we end up just busy waiting for the dma engine anyway, this commit fixes things by simply removing the dma code, resulting in smaller bug-free code. Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- drivers/mmc/sunxi_mmc.c | 141 +++--------------------------------------------- 1 file changed, 7 insertions(+), 134 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index eb7b115..d4e574f 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -16,28 +16,6 @@ #include #include -struct sunxi_mmc_des { - u32 reserved1_1:1; - u32 dic:1; /* disable interrupt on completion */ - u32 last_des:1; /* 1-this data buffer is the last buffer */ - u32 first_des:1; /* 1-data buffer is the first buffer, - 0-data buffer contained in the next - descriptor is 1st buffer */ - u32 des_chain:1; /* 1-the 2nd address in the descriptor is the - next descriptor address */ - u32 end_of_ring:1; /* 1-last descriptor flag when using dual - data buffer in descriptor */ - u32 reserved1_2:24; - u32 card_err_sum:1; /* transfer error flag */ - u32 own:1; /* des owner:1-idma owns it, 0-host owns it */ -#define SDXC_DES_NUM_SHIFT 16 -#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT) - u32 data_buf1_sz:16; - u32 data_buf2_sz:16; - u32 buf_addr_ptr1; - u32 buf_addr_ptr2; -}; - struct sunxi_mmc_host { unsigned mmc_no; uint32_t *mclkreg; @@ -189,6 +167,7 @@ static int mmc_core_init(struct mmc *mmc) /* Reset controller */ writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl); + udelay(1000); return 0; } @@ -204,6 +183,9 @@ static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data) unsigned timeout_msecs = 2000; unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); + /* Always read / write data through the CPU */ + setbits_le32(&mmchost->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB); + for (i = 0; i < (byte_cnt >> 2); i++) { while (readl(&mmchost->reg->status) & status_bit) { if (!timeout_msecs--) @@ -220,85 +202,6 @@ static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data) return 0; } -static int mmc_trans_data_by_dma(struct mmc *mmc, struct mmc_data *data) -{ - struct sunxi_mmc_host *mmchost = mmc->priv; - unsigned byte_cnt = data->blocksize * data->blocks; - unsigned char *buff; - unsigned des_idx = 0; - unsigned buff_frag_num = - (byte_cnt + SDXC_DES_BUFFER_MAX_LEN - 1) >> SDXC_DES_NUM_SHIFT; - unsigned remain; - unsigned i, rval; - ALLOC_CACHE_ALIGN_BUFFER(struct sunxi_mmc_des, pdes, buff_frag_num); - - buff = data->flags & MMC_DATA_READ ? - (unsigned char *)data->dest : (unsigned char *)data->src; - remain = byte_cnt & (SDXC_DES_BUFFER_MAX_LEN - 1); - - flush_cache((unsigned long)buff, (unsigned long)byte_cnt); - for (i = 0; i < buff_frag_num; i++, des_idx++) { - memset((void *)&pdes[des_idx], 0, sizeof(struct sunxi_mmc_des)); - pdes[des_idx].des_chain = 1; - pdes[des_idx].own = 1; - pdes[des_idx].dic = 1; - if (buff_frag_num > 1 && i != buff_frag_num - 1) - pdes[des_idx].data_buf1_sz = 0; /* 0 == max_len */ - else - pdes[des_idx].data_buf1_sz = remain; - - pdes[des_idx].buf_addr_ptr1 = - (u32) buff + i * SDXC_DES_BUFFER_MAX_LEN; - if (i == 0) - pdes[des_idx].first_des = 1; - - if (i == buff_frag_num - 1) { - pdes[des_idx].dic = 0; - pdes[des_idx].last_des = 1; - pdes[des_idx].end_of_ring = 1; - pdes[des_idx].buf_addr_ptr2 = 0; - } else { - pdes[des_idx].buf_addr_ptr2 = (u32)&pdes[des_idx + 1]; - } - } - flush_cache((unsigned long)pdes, - sizeof(struct sunxi_mmc_des) * (des_idx + 1)); - - rval = readl(&mmchost->reg->gctrl); - /* Enable DMA */ - writel(rval | SUNXI_MMC_GCTRL_DMA_RESET | SUNXI_MMC_GCTRL_DMA_ENABLE, - &mmchost->reg->gctrl); - /* Reset iDMA */ - writel(SUNXI_MMC_IDMAC_RESET, &mmchost->reg->dmac); - /* Enable iDMA */ - writel(SUNXI_MMC_IDMAC_FIXBURST | SUNXI_MMC_IDMAC_ENABLE, - &mmchost->reg->dmac); - rval = readl(&mmchost->reg->idie) & - ~(SUNXI_MMC_IDIE_TXIRQ|SUNXI_MMC_IDIE_RXIRQ); - if (data->flags & MMC_DATA_WRITE) - rval |= SUNXI_MMC_IDIE_TXIRQ; - else - rval |= SUNXI_MMC_IDIE_RXIRQ; - writel(rval, &mmchost->reg->idie); - writel((u32) pdes, &mmchost->reg->dlba); - writel((0x2 << 28) | (0x7 << 16) | (0x01 << 3), - &mmchost->reg->ftrglevel); - - return 0; -} - -static void mmc_enable_dma_accesses(struct mmc *mmc, int dma) -{ - struct sunxi_mmc_host *mmchost = mmc->priv; - - unsigned int gctrl = readl(&mmchost->reg->gctrl); - if (dma) - gctrl &= ~SUNXI_MMC_GCTRL_ACCESS_BY_AHB; - else - gctrl |= SUNXI_MMC_GCTRL_ACCESS_BY_AHB; - writel(gctrl, &mmchost->reg->gctrl); -} - static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs, unsigned int done_bit, const char *what) { @@ -327,7 +230,6 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, unsigned int timeout_msecs; int error = 0; unsigned int status = 0; - unsigned int usedma = 0; unsigned int bytecnt = 0; if (mmchost->fatal_err) @@ -378,20 +280,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, bytecnt = data->blocksize * data->blocks; debug("trans data %d bytes\n", bytecnt); -#if defined(CONFIG_MMC_SUNXI_USE_DMA) && !defined(CONFIG_SPL_BUILD) - if (bytecnt > 64) { -#else - if (0) { -#endif - usedma = 1; - mmc_enable_dma_accesses(mmc, 1); - ret = mmc_trans_data_by_dma(mmc, data); - writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); - } else { - mmc_enable_dma_accesses(mmc, 0); - writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); - ret = mmc_trans_data_by_cpu(mmc, data); - } + writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); + ret = mmc_trans_data_by_cpu(mmc, data); if (ret) { error = readl(&mmchost->reg->rint) & \ SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT; @@ -405,7 +295,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, goto out; if (data) { - timeout_msecs = usedma ? 120 * bytecnt : 120; + timeout_msecs = 120; debug("cacl timeout %x msec\n", timeout_msecs); error = mmc_rint_wait(mmc, timeout_msecs, data->blocks > 1 ? @@ -442,23 +332,6 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, debug("mmc resp 0x%08x\n", cmd->response[0]); } out: - if (data && usedma) { - /* IDMASTAREG - * IDST[0] : idma tx int - * IDST[1] : idma rx int - * IDST[2] : idma fatal bus error - * IDST[4] : idma descriptor invalid - * IDST[5] : idma error summary - * IDST[8] : idma normal interrupt sumary - * IDST[9] : idma abnormal interrupt sumary - */ - status = readl(&mmchost->reg->idst); - writel(status, &mmchost->reg->idst); - writel(0, &mmchost->reg->idie); - writel(0, &mmchost->reg->dmac); - writel(readl(&mmchost->reg->gctrl) & ~SUNXI_MMC_GCTRL_DMA_ENABLE, - &mmchost->reg->gctrl); - } if (error < 0) { writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl); mmc_update_clk(mmc); -- cgit v1.1 From b70ed300b0dcf23e46265904ddc4fbf9a72b99b8 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 9 Jun 2014 11:36:59 +0200 Subject: net: Rename and cleanup sunxi (Allwinner) emac driver There have been 3 versions of the sunxi_emac support patch during its development. Somehow version 2 ended up in upstream u-boot where as the u-boot-sunxi git repo got version 3. This bumps the version in upstream u-boot to version 3 of the patch: - Initialize MII clock earlier so mii access to allow independent use - Name change from WEMAC to EMAC to match mainline kernel & chip manual - Cosmetic code cleanup Signed-off-by: Stefan Roese Signed-off-by: Henrik Nordstrom Signed-off-by: Oliver Schinagl Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- drivers/net/Makefile | 2 +- drivers/net/sunxi_emac.c | 521 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/sunxi_wemac.c | 525 ---------------------------------------------- 3 files changed, 522 insertions(+), 526 deletions(-) create mode 100644 drivers/net/sunxi_emac.c delete mode 100644 drivers/net/sunxi_wemac.c (limited to 'drivers') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6226cb2..7cc6b6f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_DNET) += dnet.o obj-$(CONFIG_E1000) += e1000.o obj-$(CONFIG_E1000_SPI) += e1000_spi.o obj-$(CONFIG_EEPRO100) += eepro100.o +obj-$(CONFIG_SUNXI_EMAC) += sunxi_emac.o obj-$(CONFIG_ENC28J60) += enc28j60.o obj-$(CONFIG_EP93XX) += ep93xx_eth.o obj-$(CONFIG_ETHOC) += ethoc.o @@ -51,7 +52,6 @@ obj-$(CONFIG_RTL8169) += rtl8169.o obj-$(CONFIG_SH_ETHER) += sh_eth.o obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_SMC911X) += smc911x.o -obj-$(CONFIG_SUNXI_WEMAC) += sunxi_wemac.o obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c new file mode 100644 index 0000000..5a06d68 --- /dev/null +++ b/drivers/net/sunxi_emac.c @@ -0,0 +1,521 @@ +/* + * sunxi_emac.c -- Allwinner A10 ethernet driver + * + * (C) Copyright 2012, Stefan Roese + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* EMAC register */ +struct emac_regs { + u32 ctl; /* 0x00 */ + u32 tx_mode; /* 0x04 */ + u32 tx_flow; /* 0x08 */ + u32 tx_ctl0; /* 0x0c */ + u32 tx_ctl1; /* 0x10 */ + u32 tx_ins; /* 0x14 */ + u32 tx_pl0; /* 0x18 */ + u32 tx_pl1; /* 0x1c */ + u32 tx_sta; /* 0x20 */ + u32 tx_io_data; /* 0x24 */ + u32 tx_io_data1;/* 0x28 */ + u32 tx_tsvl0; /* 0x2c */ + u32 tx_tsvh0; /* 0x30 */ + u32 tx_tsvl1; /* 0x34 */ + u32 tx_tsvh1; /* 0x38 */ + u32 rx_ctl; /* 0x3c */ + u32 rx_hash0; /* 0x40 */ + u32 rx_hash1; /* 0x44 */ + u32 rx_sta; /* 0x48 */ + u32 rx_io_data; /* 0x4c */ + u32 rx_fbc; /* 0x50 */ + u32 int_ctl; /* 0x54 */ + u32 int_sta; /* 0x58 */ + u32 mac_ctl0; /* 0x5c */ + u32 mac_ctl1; /* 0x60 */ + u32 mac_ipgt; /* 0x64 */ + u32 mac_ipgr; /* 0x68 */ + u32 mac_clrt; /* 0x6c */ + u32 mac_maxf; /* 0x70 */ + u32 mac_supp; /* 0x74 */ + u32 mac_test; /* 0x78 */ + u32 mac_mcfg; /* 0x7c */ + u32 mac_mcmd; /* 0x80 */ + u32 mac_madr; /* 0x84 */ + u32 mac_mwtd; /* 0x88 */ + u32 mac_mrdd; /* 0x8c */ + u32 mac_mind; /* 0x90 */ + u32 mac_ssrr; /* 0x94 */ + u32 mac_a0; /* 0x98 */ + u32 mac_a1; /* 0x9c */ +}; + +/* SRAMC register */ +struct sunxi_sramc_regs { + u32 ctrl0; + u32 ctrl1; +}; + +/* 0: Disable 1: Aborted frame enable(default) */ +#define EMAC_TX_AB_M (0x1 << 0) +/* 0: CPU 1: DMA(default) */ +#define EMAC_TX_TM (0x1 << 1) + +#define EMAC_TX_SETUP (0) + +/* 0: DRQ asserted 1: DRQ automatically(default) */ +#define EMAC_RX_DRQ_MODE (0x1 << 1) +/* 0: CPU 1: DMA(default) */ +#define EMAC_RX_TM (0x1 << 2) +/* 0: Normal(default) 1: Pass all Frames */ +#define EMAC_RX_PA (0x1 << 4) +/* 0: Normal(default) 1: Pass Control Frames */ +#define EMAC_RX_PCF (0x1 << 5) +/* 0: Normal(default) 1: Pass Frames with CRC Error */ +#define EMAC_RX_PCRCE (0x1 << 6) +/* 0: Normal(default) 1: Pass Frames with Length Error */ +#define EMAC_RX_PLE (0x1 << 7) +/* 0: Normal 1: Pass Frames length out of range(default) */ +#define EMAC_RX_POR (0x1 << 8) +/* 0: Not accept 1: Accept unicast Packets(default) */ +#define EMAC_RX_UCAD (0x1 << 16) +/* 0: Normal(default) 1: DA Filtering */ +#define EMAC_RX_DAF (0x1 << 17) +/* 0: Not accept 1: Accept multicast Packets(default) */ +#define EMAC_RX_MCO (0x1 << 20) +/* 0: Disable(default) 1: Enable Hash filter */ +#define EMAC_RX_MHF (0x1 << 21) +/* 0: Not accept 1: Accept Broadcast Packets(default) */ +#define EMAC_RX_BCO (0x1 << 22) +/* 0: Disable(default) 1: Enable SA Filtering */ +#define EMAC_RX_SAF (0x1 << 24) +/* 0: Normal(default) 1: Inverse Filtering */ +#define EMAC_RX_SAIF (0x1 << 25) + +#define EMAC_RX_SETUP (EMAC_RX_POR | EMAC_RX_UCAD | EMAC_RX_DAF | \ + EMAC_RX_MCO | EMAC_RX_BCO) + +/* 0: Disable 1: Enable Receive Flow Control(default) */ +#define EMAC_MAC_CTL0_RFC (0x1 << 2) +/* 0: Disable 1: Enable Transmit Flow Control(default) */ +#define EMAC_MAC_CTL0_TFC (0x1 << 3) + +#define EMAC_MAC_CTL0_SETUP (EMAC_MAC_CTL0_RFC | EMAC_MAC_CTL0_TFC) + +/* 0: Disable 1: Enable MAC Frame Length Checking(default) */ +#define EMAC_MAC_CTL1_FLC (0x1 << 1) +/* 0: Disable(default) 1: Enable Huge Frame */ +#define EMAC_MAC_CTL1_HF (0x1 << 2) +/* 0: Disable(default) 1: Enable MAC Delayed CRC */ +#define EMAC_MAC_CTL1_DCRC (0x1 << 3) +/* 0: Disable 1: Enable MAC CRC(default) */ +#define EMAC_MAC_CTL1_CRC (0x1 << 4) +/* 0: Disable 1: Enable MAC PAD Short frames(default) */ +#define EMAC_MAC_CTL1_PC (0x1 << 5) +/* 0: Disable(default) 1: Enable MAC PAD Short frames and append CRC */ +#define EMAC_MAC_CTL1_VC (0x1 << 6) +/* 0: Disable(default) 1: Enable MAC auto detect Short frames */ +#define EMAC_MAC_CTL1_ADP (0x1 << 7) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_PRE (0x1 << 8) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_LPE (0x1 << 9) +/* 0: Disable(default) 1: Enable no back off */ +#define EMAC_MAC_CTL1_NB (0x1 << 12) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_BNB (0x1 << 13) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_ED (0x1 << 14) + +#define EMAC_MAC_CTL1_SETUP (EMAC_MAC_CTL1_FLC | EMAC_MAC_CTL1_CRC | \ + EMAC_MAC_CTL1_PC) + +#define EMAC_MAC_IPGT 0x15 + +#define EMAC_MAC_NBTB_IPG1 0xc +#define EMAC_MAC_NBTB_IPG2 0x12 + +#define EMAC_MAC_CW 0x37 +#define EMAC_MAC_RM 0xf + +#define EMAC_MAC_MFL 0x0600 + +/* Receive status */ +#define EMAC_CRCERR (0x1 << 4) +#define EMAC_LENERR (0x3 << 5) + +#define DMA_CPU_TRRESHOLD 2000 + +struct emac_eth_dev { + u32 speed; + u32 duplex; + u32 phy_configured; + int link_printed; +}; + +struct emac_rxhdr { + s16 rx_len; + u16 rx_status; +}; + +static void emac_inblk_32bit(void *reg, void *data, int count) +{ + int cnt = (count + 3) >> 2; + + if (cnt) { + u32 *buf = data; + + do { + u32 x = readl(reg); + *buf++ = x; + } while (--cnt); + } +} + +static void emac_outblk_32bit(void *reg, void *data, int count) +{ + int cnt = (count + 3) >> 2; + + if (cnt) { + const u32 *buf = data; + + do { + writel(*buf++, reg); + } while (--cnt); + } +} + +/* Read a word from phyxcer */ +static int emac_phy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + + /* issue the phy address and reg */ + writel(addr << 8 | reg, ®s->mac_madr); + + /* pull up the phy io line */ + writel(0x1, ®s->mac_mcmd); + + /* Wait read complete */ + mdelay(1); + + /* push down the phy io line */ + writel(0x0, ®s->mac_mcmd); + + /* and write data */ + *value = readl(®s->mac_mrdd); + + return 0; +} + +/* Write a word to phyxcer */ +static int emac_phy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + + /* issue the phy address and reg */ + writel(addr << 8 | reg, ®s->mac_madr); + + /* pull up the phy io line */ + writel(0x1, ®s->mac_mcmd); + + /* Wait write complete */ + mdelay(1); + + /* push down the phy io line */ + writel(0x0, ®s->mac_mcmd); + + /* and write data */ + writel(value, ®s->mac_mwtd); + + return 0; +} + +static void emac_setup(struct eth_device *dev) +{ + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + u32 reg_val; + u16 phy_val; + u32 duplex_flag; + + /* Set up TX */ + writel(EMAC_TX_SETUP, ®s->tx_mode); + + /* Set up RX */ + writel(EMAC_RX_SETUP, ®s->rx_ctl); + + /* Set MAC */ + /* Set MAC CTL0 */ + writel(EMAC_MAC_CTL0_SETUP, ®s->mac_ctl0); + + /* Set MAC CTL1 */ + emac_phy_read(dev->name, 1, 0, &phy_val); + debug("PHY SETUP, reg 0 value: %x\n", phy_val); + duplex_flag = !!(phy_val & (1 << 8)); + + reg_val = 0; + if (duplex_flag) + reg_val = (0x1 << 0); + writel(EMAC_MAC_CTL1_SETUP | reg_val, ®s->mac_ctl1); + + /* Set up IPGT */ + writel(EMAC_MAC_IPGT, ®s->mac_ipgt); + + /* Set up IPGR */ + writel(EMAC_MAC_NBTB_IPG2 | (EMAC_MAC_NBTB_IPG1 << 8), ®s->mac_ipgr); + + /* Set up Collison window */ + writel(EMAC_MAC_RM | (EMAC_MAC_CW << 8), ®s->mac_clrt); + + /* Set up Max Frame Length */ + writel(EMAC_MAC_MFL, ®s->mac_maxf); +} + +static void emac_reset(struct eth_device *dev) +{ + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + + debug("resetting device\n"); + + /* RESET device */ + writel(0, ®s->ctl); + udelay(200); + + writel(1, ®s->ctl); + udelay(200); +} + +static int sunxi_emac_eth_init(struct eth_device *dev, bd_t *bd) +{ + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + struct emac_eth_dev *priv = dev->priv; + u16 phy_reg; + + /* Init EMAC */ + + /* Flush RX FIFO */ + setbits_le32(®s->rx_ctl, 0x8); + udelay(1); + + /* Init MAC */ + + /* Soft reset MAC */ + clrbits_le32(®s->mac_ctl0, 0x1 << 15); + + /* Clear RX counter */ + writel(0x0, ®s->rx_fbc); + udelay(1); + + /* Set up EMAC */ + emac_setup(dev); + + writel(dev->enetaddr[0] << 16 | dev->enetaddr[1] << 8 | + dev->enetaddr[2], ®s->mac_a1); + writel(dev->enetaddr[3] << 16 | dev->enetaddr[4] << 8 | + dev->enetaddr[5], ®s->mac_a0); + + mdelay(1); + + emac_reset(dev); + + /* PHY POWER UP */ + emac_phy_read(dev->name, 1, 0, &phy_reg); + emac_phy_write(dev->name, 1, 0, phy_reg & (~(0x1 << 11))); + mdelay(1); + + emac_phy_read(dev->name, 1, 0, &phy_reg); + + priv->speed = miiphy_speed(dev->name, 0); + priv->duplex = miiphy_duplex(dev->name, 0); + + /* Print link status only once */ + if (!priv->link_printed) { + printf("ENET Speed is %d Mbps - %s duplex connection\n", + priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL"); + priv->link_printed = 1; + } + + /* Set EMAC SPEED depend on PHY */ + clrsetbits_le32(®s->mac_supp, 1 << 8, + ((phy_reg & (0x1 << 13)) >> 13) << 8); + + /* Set duplex depend on phy */ + clrsetbits_le32(®s->mac_ctl1, 1 << 0, + ((phy_reg & (0x1 << 8)) >> 8) << 0); + + /* Enable RX/TX */ + setbits_le32(®s->ctl, 0x7); + + return 0; +} + +static void sunxi_emac_eth_halt(struct eth_device *dev) +{ + /* Nothing to do here */ +} + +static int sunxi_emac_eth_recv(struct eth_device *dev) +{ + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + struct emac_rxhdr rxhdr; + u32 rxcount; + u32 reg_val; + int rx_len; + int rx_status; + int good_packet; + + /* Check packet ready or not */ + + /* Race warning: The first packet might arrive with + * the interrupts disabled, but the second will fix + */ + rxcount = readl(®s->rx_fbc); + if (!rxcount) { + /* Had one stuck? */ + rxcount = readl(®s->rx_fbc); + if (!rxcount) + return 0; + } + + reg_val = readl(®s->rx_io_data); + if (reg_val != 0x0143414d) { + /* Disable RX */ + clrbits_le32(®s->ctl, 0x1 << 2); + + /* Flush RX FIFO */ + setbits_le32(®s->rx_ctl, 0x1 << 3); + while (readl(®s->rx_ctl) & (0x1 << 3)) + ; + + /* Enable RX */ + setbits_le32(®s->ctl, 0x1 << 2); + + return 0; + } + + /* A packet ready now + * Get status/length + */ + good_packet = 1; + + emac_inblk_32bit(®s->rx_io_data, &rxhdr, sizeof(rxhdr)); + + rx_len = rxhdr.rx_len; + rx_status = rxhdr.rx_status; + + /* Packet Status check */ + if (rx_len < 0x40) { + good_packet = 0; + debug("RX: Bad Packet (runt)\n"); + } + + /* rx_status is identical to RSR register. */ + if (0 & rx_status & (EMAC_CRCERR | EMAC_LENERR)) { + good_packet = 0; + if (rx_status & EMAC_CRCERR) + printf("crc error\n"); + if (rx_status & EMAC_LENERR) + printf("length error\n"); + } + + /* Move data from EMAC */ + if (good_packet) { + if (rx_len > DMA_CPU_TRRESHOLD) { + printf("Received packet is too big (len=%d)\n", rx_len); + } else { + emac_inblk_32bit((void *)®s->rx_io_data, + NetRxPackets[0], rx_len); + + /* Pass to upper layer */ + NetReceive(NetRxPackets[0], rx_len); + return rx_len; + } + } + + return 0; +} + +static int sunxi_emac_eth_send(struct eth_device *dev, void *packet, int len) +{ + struct emac_regs *regs = (struct emac_regs *)dev->iobase; + + /* Select channel 0 */ + writel(0, ®s->tx_ins); + + /* Write packet */ + emac_outblk_32bit((void *)®s->tx_io_data, packet, len); + + /* Set TX len */ + writel(len, ®s->tx_pl0); + + /* Start translate from fifo to phy */ + setbits_le32(®s->tx_ctl0, 1); + + return 0; +} + +int sunxi_emac_initialize(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_sramc_regs *sram = + (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE; + struct emac_regs *regs = + (struct emac_regs *)SUNXI_EMAC_BASE; + struct eth_device *dev; + struct emac_eth_dev *priv; + int pin; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) + return -ENOMEM; + + priv = (struct emac_eth_dev *)malloc(sizeof(struct emac_eth_dev)); + if (!priv) { + free(dev); + return -ENOMEM; + } + + memset(dev, 0, sizeof(*dev)); + memset(priv, 0, sizeof(struct emac_eth_dev)); + + /* Map SRAM to EMAC */ + setbits_le32(&sram->ctrl1, 0x5 << 2); + + /* Configure pin mux settings for MII Ethernet */ + for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++) + sunxi_gpio_set_cfgpin(pin, SUNXI_GPA0_EMAC); + + /* Set up clock gating */ + setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_EMAC); + + /* Set MII clock */ + clrsetbits_le32(®s->mac_mcfg, 0xf << 2, 0xd << 2); + + dev->iobase = (int)regs; + dev->priv = priv; + dev->init = sunxi_emac_eth_init; + dev->halt = sunxi_emac_eth_halt; + dev->send = sunxi_emac_eth_send; + dev->recv = sunxi_emac_eth_recv; + strcpy(dev->name, "emac"); + + eth_register(dev); + + miiphy_register(dev->name, emac_phy_read, emac_phy_write); + + return 0; +} diff --git a/drivers/net/sunxi_wemac.c b/drivers/net/sunxi_wemac.c deleted file mode 100644 index 699a381..0000000 --- a/drivers/net/sunxi_wemac.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * sunxi_wemac.c -- Allwinner A10 ethernet driver - * - * (C) Copyright 2012, Stefan Roese - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* EMAC register */ -struct wemac_regs { - u32 ctl; /* 0x00 */ - u32 tx_mode; /* 0x04 */ - u32 tx_flow; /* 0x08 */ - u32 tx_ctl0; /* 0x0c */ - u32 tx_ctl1; /* 0x10 */ - u32 tx_ins; /* 0x14 */ - u32 tx_pl0; /* 0x18 */ - u32 tx_pl1; /* 0x1c */ - u32 tx_sta; /* 0x20 */ - u32 tx_io_data; /* 0x24 */ - u32 tx_io_data1; /* 0x28 */ - u32 tx_tsvl0; /* 0x2c */ - u32 tx_tsvh0; /* 0x30 */ - u32 tx_tsvl1; /* 0x34 */ - u32 tx_tsvh1; /* 0x38 */ - u32 rx_ctl; /* 0x3c */ - u32 rx_hash0; /* 0x40 */ - u32 rx_hash1; /* 0x44 */ - u32 rx_sta; /* 0x48 */ - u32 rx_io_data; /* 0x4c */ - u32 rx_fbc; /* 0x50 */ - u32 int_ctl; /* 0x54 */ - u32 int_sta; /* 0x58 */ - u32 mac_ctl0; /* 0x5c */ - u32 mac_ctl1; /* 0x60 */ - u32 mac_ipgt; /* 0x64 */ - u32 mac_ipgr; /* 0x68 */ - u32 mac_clrt; /* 0x6c */ - u32 mac_maxf; /* 0x70 */ - u32 mac_supp; /* 0x74 */ - u32 mac_test; /* 0x78 */ - u32 mac_mcfg; /* 0x7c */ - u32 mac_mcmd; /* 0x80 */ - u32 mac_madr; /* 0x84 */ - u32 mac_mwtd; /* 0x88 */ - u32 mac_mrdd; /* 0x8c */ - u32 mac_mind; /* 0x90 */ - u32 mac_ssrr; /* 0x94 */ - u32 mac_a0; /* 0x98 */ - u32 mac_a1; /* 0x9c */ -}; - -/* SRAMC register */ -struct sunxi_sramc_regs { - u32 ctrl0; - u32 ctrl1; -}; - -/* 0: Disable 1: Aborted frame enable(default) */ -#define EMAC_TX_AB_M (0x1 << 0) -/* 0: CPU 1: DMA(default) */ -#define EMAC_TX_TM (0x1 << 1) - -#define EMAC_TX_SETUP (0) - -/* 0: DRQ asserted 1: DRQ automatically(default) */ -#define EMAC_RX_DRQ_MODE (0x1 << 1) -/* 0: CPU 1: DMA(default) */ -#define EMAC_RX_TM (0x1 << 2) -/* 0: Normal(default) 1: Pass all Frames */ -#define EMAC_RX_PA (0x1 << 4) -/* 0: Normal(default) 1: Pass Control Frames */ -#define EMAC_RX_PCF (0x1 << 5) -/* 0: Normal(default) 1: Pass Frames with CRC Error */ -#define EMAC_RX_PCRCE (0x1 << 6) -/* 0: Normal(default) 1: Pass Frames with Length Error */ -#define EMAC_RX_PLE (0x1 << 7) -/* 0: Normal 1: Pass Frames length out of range(default) */ -#define EMAC_RX_POR (0x1 << 8) -/* 0: Not accept 1: Accept unicast Packets(default) */ -#define EMAC_RX_UCAD (0x1 << 16) -/* 0: Normal(default) 1: DA Filtering */ -#define EMAC_RX_DAF (0x1 << 17) -/* 0: Not accept 1: Accept multicast Packets(default) */ -#define EMAC_RX_MCO (0x1 << 20) -/* 0: Disable(default) 1: Enable Hash filter */ -#define EMAC_RX_MHF (0x1 << 21) -/* 0: Not accept 1: Accept Broadcast Packets(default) */ -#define EMAC_RX_BCO (0x1 << 22) -/* 0: Disable(default) 1: Enable SA Filtering */ -#define EMAC_RX_SAF (0x1 << 24) -/* 0: Normal(default) 1: Inverse Filtering */ -#define EMAC_RX_SAIF (0x1 << 25) - -#define EMAC_RX_SETUP (EMAC_RX_POR | EMAC_RX_UCAD | EMAC_RX_DAF | \ - EMAC_RX_MCO | EMAC_RX_BCO) - -/* 0: Disable 1: Enable Receive Flow Control(default) */ -#define EMAC_MAC_CTL0_RFC (0x1 << 2) -/* 0: Disable 1: Enable Transmit Flow Control(default) */ -#define EMAC_MAC_CTL0_TFC (0x1 << 3) - -#define EMAC_MAC_CTL0_SETUP (EMAC_MAC_CTL0_RFC | EMAC_MAC_CTL0_TFC) - -/* 0: Disable 1: Enable MAC Frame Length Checking(default) */ -#define EMAC_MAC_CTL1_FLC (0x1 << 1) -/* 0: Disable(default) 1: Enable Huge Frame */ -#define EMAC_MAC_CTL1_HF (0x1 << 2) -/* 0: Disable(default) 1: Enable MAC Delayed CRC */ -#define EMAC_MAC_CTL1_DCRC (0x1 << 3) -/* 0: Disable 1: Enable MAC CRC(default) */ -#define EMAC_MAC_CTL1_CRC (0x1 << 4) -/* 0: Disable 1: Enable MAC PAD Short frames(default) */ -#define EMAC_MAC_CTL1_PC (0x1 << 5) -/* 0: Disable(default) 1: Enable MAC PAD Short frames and append CRC */ -#define EMAC_MAC_CTL1_VC (0x1 << 6) -/* 0: Disable(default) 1: Enable MAC auto detect Short frames */ -#define EMAC_MAC_CTL1_ADP (0x1 << 7) -/* 0: Disable(default) 1: Enable */ -#define EMAC_MAC_CTL1_PRE (0x1 << 8) -/* 0: Disable(default) 1: Enable */ -#define EMAC_MAC_CTL1_LPE (0x1 << 9) -/* 0: Disable(default) 1: Enable no back off */ -#define EMAC_MAC_CTL1_NB (0x1 << 12) -/* 0: Disable(default) 1: Enable */ -#define EMAC_MAC_CTL1_BNB (0x1 << 13) -/* 0: Disable(default) 1: Enable */ -#define EMAC_MAC_CTL1_ED (0x1 << 14) - -#define EMAC_MAC_CTL1_SETUP (EMAC_MAC_CTL1_FLC | EMAC_MAC_CTL1_CRC | \ - EMAC_MAC_CTL1_PC) - -#define EMAC_MAC_IPGT 0x15 - -#define EMAC_MAC_NBTB_IPG1 0xC -#define EMAC_MAC_NBTB_IPG2 0x12 - -#define EMAC_MAC_CW 0x37 -#define EMAC_MAC_RM 0xF - -#define EMAC_MAC_MFL 0x0600 - -/* Receive status */ -#define EMAC_CRCERR (1 << 4) -#define EMAC_LENERR (3 << 5) - -#define DMA_CPU_TRRESHOLD 2000 - -struct wemac_eth_dev { - u32 speed; - u32 duplex; - u32 phy_configured; - int link_printed; -}; - -struct wemac_rxhdr { - s16 rx_len; - u16 rx_status; -}; - -static void wemac_inblk_32bit(void *reg, void *data, int count) -{ - int cnt = (count + 3) >> 2; - - if (cnt) { - u32 *buf = data; - - do { - u32 x = readl(reg); - *buf++ = x; - } while (--cnt); - } -} - -static void wemac_outblk_32bit(void *reg, void *data, int count) -{ - int cnt = (count + 3) >> 2; - - if (cnt) { - const u32 *buf = data; - - do { - writel(*buf++, reg); - } while (--cnt); - } -} - -/* - * Read a word from phyxcer - */ -static int wemac_phy_read(const char *devname, unsigned char addr, - unsigned char reg, unsigned short *value) -{ - struct eth_device *dev = eth_get_dev_by_name(devname); - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - - /* issue the phy address and reg */ - writel(addr << 8 | reg, ®s->mac_madr); - - /* pull up the phy io line */ - writel(0x1, ®s->mac_mcmd); - - /* Wait read complete */ - mdelay(1); - - /* push down the phy io line */ - writel(0x0, ®s->mac_mcmd); - - /* and write data */ - *value = readl(®s->mac_mrdd); - - return 0; -} - -/* - * Write a word to phyxcer - */ -static int wemac_phy_write(const char *devname, unsigned char addr, - unsigned char reg, unsigned short value) -{ - struct eth_device *dev = eth_get_dev_by_name(devname); - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - - /* issue the phy address and reg */ - writel(addr << 8 | reg, ®s->mac_madr); - - /* pull up the phy io line */ - writel(0x1, ®s->mac_mcmd); - - /* Wait write complete */ - mdelay(1); - - /* push down the phy io line */ - writel(0x0, ®s->mac_mcmd); - - /* and write data */ - writel(value, ®s->mac_mwtd); - - return 0; -} - -static void emac_setup(struct eth_device *dev) -{ - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - u32 reg_val; - u16 phy_val; - u32 duplex_flag; - - /* Set up TX */ - writel(EMAC_TX_SETUP, ®s->tx_mode); - - /* Set up RX */ - writel(EMAC_RX_SETUP, ®s->rx_ctl); - - /* Set MAC */ - /* Set MAC CTL0 */ - writel(EMAC_MAC_CTL0_SETUP, ®s->mac_ctl0); - - /* Set MAC CTL1 */ - wemac_phy_read(dev->name, 1, 0, &phy_val); - debug("PHY SETUP, reg 0 value: %x\n", phy_val); - duplex_flag = !!(phy_val & (1 << 8)); - - reg_val = 0; - if (duplex_flag) - reg_val = (0x1 << 0); - writel(EMAC_MAC_CTL1_SETUP | reg_val, ®s->mac_ctl1); - - /* Set up IPGT */ - writel(EMAC_MAC_IPGT, ®s->mac_ipgt); - - /* Set up IPGR */ - writel(EMAC_MAC_NBTB_IPG2 | (EMAC_MAC_NBTB_IPG1 << 8), ®s->mac_ipgr); - - /* Set up Collison window */ - writel(EMAC_MAC_RM | (EMAC_MAC_CW << 8), ®s->mac_clrt); - - /* Set up Max Frame Length */ - writel(EMAC_MAC_MFL, ®s->mac_maxf); -} - -static void wemac_reset(struct eth_device *dev) -{ - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - - debug("resetting device\n"); - - /* RESET device */ - writel(0, ®s->ctl); - udelay(200); - - writel(1, ®s->ctl); - udelay(200); -} - -static int sunxi_wemac_eth_init(struct eth_device *dev, bd_t *bd) -{ - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - struct wemac_eth_dev *priv = dev->priv; - u16 phy_reg; - - /* Init EMAC */ - - /* Flush RX FIFO */ - setbits_le32(®s->rx_ctl, 0x8); - udelay(1); - - /* Init MAC */ - - /* Soft reset MAC */ - clrbits_le32(®s->mac_ctl0, 1 << 15); - - /* Set MII clock */ - clrsetbits_le32(®s->mac_mcfg, 0xf << 2, 0xd << 2); - - /* Clear RX counter */ - writel(0x0, ®s->rx_fbc); - udelay(1); - - /* Set up EMAC */ - emac_setup(dev); - - writel(dev->enetaddr[0] << 16 | dev->enetaddr[1] << 8 | - dev->enetaddr[2], ®s->mac_a1); - writel(dev->enetaddr[3] << 16 | dev->enetaddr[4] << 8 | - dev->enetaddr[5], ®s->mac_a0); - - mdelay(1); - - wemac_reset(dev); - - /* PHY POWER UP */ - wemac_phy_read(dev->name, 1, 0, &phy_reg); - wemac_phy_write(dev->name, 1, 0, phy_reg & (~(1 << 11))); - mdelay(1); - - wemac_phy_read(dev->name, 1, 0, &phy_reg); - - priv->speed = miiphy_speed(dev->name, 0); - priv->duplex = miiphy_duplex(dev->name, 0); - - /* Print link status only once */ - if (!priv->link_printed) { - printf("ENET Speed is %d Mbps - %s duplex connection\n", - priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL"); - priv->link_printed = 1; - } - - /* Set EMAC SPEED depend on PHY */ - clrsetbits_le32(®s->mac_supp, 1 << 8, - ((phy_reg & (1 << 13)) >> 13) << 8); - - /* Set duplex depend on phy */ - clrsetbits_le32(®s->mac_ctl1, 1 << 0, - ((phy_reg & (1 << 8)) >> 8) << 0); - - /* Enable RX/TX */ - setbits_le32(®s->ctl, 0x7); - - return 0; -} - -static void sunxi_wemac_eth_halt(struct eth_device *dev) -{ - /* Nothing to do here */ -} - -static int sunxi_wemac_eth_recv(struct eth_device *dev) -{ - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - struct wemac_rxhdr rxhdr; - u32 rxcount; - u32 reg_val; - int rx_len; - int rx_status; - int good_packet; - - /* Check packet ready or not */ - - /* - * Race warning: The first packet might arrive with - * the interrupts disabled, but the second will fix - */ - rxcount = readl(®s->rx_fbc); - if (!rxcount) { - /* Had one stuck? */ - rxcount = readl(®s->rx_fbc); - if (!rxcount) - return 0; - } - - reg_val = readl(®s->rx_io_data); - if (reg_val != 0x0143414d) { - /* Disable RX */ - clrbits_le32(®s->ctl, 1 << 2); - - /* Flush RX FIFO */ - setbits_le32(®s->rx_ctl, 1 << 3); - while (readl(®s->rx_ctl) & (1 << 3)) - ; - - /* Enable RX */ - setbits_le32(®s->ctl, 1 << 2); - - return 0; - } - - /* - * A packet ready now - * Get status/length - */ - good_packet = 1; - - wemac_inblk_32bit(®s->rx_io_data, &rxhdr, sizeof(rxhdr)); - - rx_len = rxhdr.rx_len; - rx_status = rxhdr.rx_status; - - /* Packet Status check */ - if (rx_len < 0x40) { - good_packet = 0; - debug("RX: Bad Packet (runt)\n"); - } - - /* rx_status is identical to RSR register. */ - if (0 & rx_status & (EMAC_CRCERR | EMAC_LENERR)) { - good_packet = 0; - if (rx_status & EMAC_CRCERR) - printf("crc error\n"); - if (rx_status & EMAC_LENERR) - printf("length error\n"); - } - - /* Move data from WEMAC */ - if (good_packet) { - if (rx_len > DMA_CPU_TRRESHOLD) { - printf("Received packet is too big (len=%d)\n", rx_len); - } else { - wemac_inblk_32bit((void *)®s->rx_io_data, - NetRxPackets[0], rx_len); - - /* Pass to upper layer */ - NetReceive(NetRxPackets[0], rx_len); - return rx_len; - } - } - - return 0; -} - -static int sunxi_wemac_eth_send(struct eth_device *dev, void *packet, int len) -{ - struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; - - /* Select channel 0 */ - writel(0, ®s->tx_ins); - - /* Write packet */ - wemac_outblk_32bit((void *)®s->tx_io_data, packet, len); - - /* Set TX len */ - writel(len, ®s->tx_pl0); - - /* Start translate from fifo to phy */ - setbits_le32(®s->tx_ctl0, 1); - - return 0; -} - -int sunxi_wemac_initialize(void) -{ - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_sramc_regs *sram = - (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE; - struct eth_device *dev; - struct wemac_eth_dev *priv; - int pin; - - dev = malloc(sizeof(*dev)); - if (dev == NULL) - return -ENOMEM; - - priv = (struct wemac_eth_dev *)malloc(sizeof(struct wemac_eth_dev)); - if (!priv) { - free(dev); - return -ENOMEM; - } - - memset(dev, 0, sizeof(*dev)); - memset(priv, 0, sizeof(struct wemac_eth_dev)); - - /* Map SRAM to EMAC */ - setbits_le32(&sram->ctrl1, 0x5 << 2); - - /* Configure pin mux settings for MII Ethernet */ - for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++) - sunxi_gpio_set_cfgpin(pin, 2); - - /* Set up clock gating */ - setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_EMAC); - - dev->iobase = SUNXI_EMAC_BASE; - dev->priv = priv; - dev->init = sunxi_wemac_eth_init; - dev->halt = sunxi_wemac_eth_halt; - dev->send = sunxi_wemac_eth_send; - dev->recv = sunxi_wemac_eth_recv; - strcpy(dev->name, "wemac"); - - eth_register(dev); - - miiphy_register(dev->name, wemac_phy_read, wemac_phy_write); - - return 0; -} -- cgit v1.1 From f7c105353593907da7cbcb1590d5c1c616b7c91e Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Mon, 30 Jun 2014 09:12:09 +0200 Subject: i2c, omap24xx: add i2c deblock sequenz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a bus busy is detected when intializing the driver, toggle 9 times the scl pin. Therefore enable the test mode of the controller, in which the scl, sda pins can be controlled manually. Tested on the siemens boards pxm2, rut and dxr2. Signed-off-by: Heiko Schocher Cc: Tom Rini Cc: Hannes Petermaier Cc: Lubomir Popov Cc: Steve Sakoman Cc: Sandeep Paulraj Cc: Vincent Stehlé Cc: Samuel Egli --- drivers/i2c/omap24xx_i2c.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index a39b591..0f1e35c 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -153,11 +153,60 @@ static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed) return 0; } + +static void omap24_i2c_deblock(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int i; + u16 systest; + u16 orgsystest; + + /* set test mode ST_EN = 1 */ + orgsystest = readw(&i2c_base->systest); + systest = orgsystest; + /* enable testmode */ + systest |= I2C_SYSTEST_ST_EN; + writew(systest, &i2c_base->systest); + systest &= ~I2C_SYSTEST_TMODE_MASK; + systest |= 3 << I2C_SYSTEST_TMODE_SHIFT; + writew(systest, &i2c_base->systest); + + /* set SCL, SDA = 1 */ + systest |= I2C_SYSTEST_SCL_O | I2C_SYSTEST_SDA_O; + writew(systest, &i2c_base->systest); + udelay(10); + + /* toggle scl 9 clocks */ + for (i = 0; i < 9; i++) { + /* SCL = 0 */ + systest &= ~I2C_SYSTEST_SCL_O; + writew(systest, &i2c_base->systest); + udelay(10); + /* SCL = 1 */ + systest |= I2C_SYSTEST_SCL_O; + writew(systest, &i2c_base->systest); + udelay(10); + } + + /* send stop */ + systest &= ~I2C_SYSTEST_SDA_O; + writew(systest, &i2c_base->systest); + udelay(10); + systest |= I2C_SYSTEST_SCL_O | I2C_SYSTEST_SDA_O; + writew(systest, &i2c_base->systest); + udelay(10); + + /* restore original mode */ + writew(orgsystest, &i2c_base->systest); +} + static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { struct i2c *i2c_base = omap24_get_base(adap); int timeout = I2C_TIMEOUT; + int deblock = 1; +retry: if (readw(&i2c_base->con) & I2C_CON_EN) { writew(0, &i2c_base->con); udelay(50000); @@ -194,6 +243,14 @@ static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) udelay(1000); flush_fifo(adap); writew(0xFFFF, &i2c_base->stat); + + /* Handle possible failed I2C state */ + if (wait_for_bb(adap)) + if (deblock == 1) { + omap24_i2c_deblock(adap); + deblock = 0; + goto retry; + } } static void flush_fifo(struct i2c_adapter *adap) -- cgit v1.1 From a17fd10fb516df3a0b00fcceb8678de2689951fc Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Mon, 7 Jul 2014 12:17:48 +0800 Subject: fsl_i2c: add support for 3rd and 4th I2C Add support for 3rd and 4th I2C. Signed-off-by: Shengzhou Liu --- drivers/i2c/fsl_i2c.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index aa159f8..811033b 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -46,10 +46,16 @@ DECLARE_GLOBAL_DATA_PTR; -static const struct fsl_i2c *i2c_dev[2] = { +static const struct fsl_i2c *i2c_dev[4] = { (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET), #ifdef CONFIG_SYS_FSL_I2C2_OFFSET - (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET) + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET), +#endif +#ifdef CONFIG_SYS_FSL_I2C3_OFFSET + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C3_OFFSET), +#endif +#ifdef CONFIG_SYS_FSL_I2C4_OFFSET + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C4_OFFSET) #endif }; @@ -539,3 +545,15 @@ U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE, 1) #endif +#ifdef CONFIG_SYS_FSL_I2C3_OFFSET +U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C3_SPEED, CONFIG_SYS_FSL_I2C3_SLAVE, + 2) +#endif +#ifdef CONFIG_SYS_FSL_I2C4_OFFSET +U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C4_SPEED, CONFIG_SYS_FSL_I2C4_SLAVE, + 3) +#endif -- cgit v1.1 From 0db2bbdc04c7ba41861e686acb815fce5a227a01 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Jun 2014 22:55:48 +0200 Subject: mvtwsi: convert to CONFIG_SYS_I2C framework Note this has only been tested on Allwinner sunxi devices (support for which gets introduced by a later patch). The kirkwood changes have been compile tested using the wireless_space board config, the orion5x changes have been compile tested using the edminiv2 board config. Signed-off-by: Hans de Goede Acked-by: Heiko Schocher --- drivers/i2c/Makefile | 2 +- drivers/i2c/mvtwsi.c | 70 ++++++++++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index e33586d..61e9f3c 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o obj-$(CONFIG_DW_I2C) += designware_i2c.o -obj-$(CONFIG_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_I2C_MV) += mv_i2c.o obj-$(CONFIG_I2C_MXS) += mxs_i2c.o obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o @@ -19,6 +18,7 @@ obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o +obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 5ba0e03..c8b5425 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -220,11 +220,10 @@ static int twsi_stop(int status) /* * Reset controller. - * Called at end of i2c_init unsuccessful i2c transactions. * Controller reset also resets the baud rate and slave address, so - * re-establish them. + * they must be re-established afterwards. */ -static void twsi_reset(u8 baud_rate, u8 slave_address) +static void twsi_reset(struct i2c_adapter *adap) { /* ensure controller will be enabled by any twsi*() function */ twsi_control_flags = MVTWSI_CONTROL_TWSIEN; @@ -232,23 +231,17 @@ static void twsi_reset(u8 baud_rate, u8 slave_address) writel(0, &twsi->soft_reset); /* wait 2 ms -- this is what the Marvell LSP does */ udelay(20000); - /* set baud rate */ - writel(baud_rate, &twsi->baudrate); - /* set slave address even though we don't use it */ - writel(slave_address, &twsi->slave_address); - writel(0, &twsi->xtnd_slave_addr); - /* assert STOP but don't care for the result */ - (void) twsi_stop(0); } /* * I2C init called by cmd_i2c when doing 'i2c reset'. * Sets baud to the highest possible value not exceeding requested one. */ -void i2c_init(int requested_speed, int slaveadd) +static unsigned int twsi_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int requested_speed) { - int tmp_speed, highest_speed, n, m; - int baud = 0x44; /* baudrate at controller reset */ + unsigned int tmp_speed, highest_speed, n, m; + unsigned int baud = 0x44; /* baudrate at controller reset */ /* use actual speed to collect progressively higher values */ highest_speed = 0; @@ -263,8 +256,21 @@ void i2c_init(int requested_speed, int slaveadd) } } } + writel(baud, &twsi->baudrate); + return 0; +} + +static void twsi_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ /* reset controller */ - twsi_reset(baud, slaveadd); + twsi_reset(adap); + /* set speed */ + twsi_i2c_set_bus_speed(adap, speed); + /* set slave address even though we don't use it */ + writel(slaveadd, &twsi->slave_address); + writel(0, &twsi->xtnd_slave_addr); + /* assert STOP but don't care for the result */ + (void) twsi_stop(0); } /* @@ -294,7 +300,7 @@ static int i2c_begin(int expected_start_status, u8 addr) * I2C probe called by cmd_i2c when doing 'i2c probe'. * Begin read, nak data byte, end. */ -int i2c_probe(uchar chip) +static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip) { u8 dummy_byte; int status; @@ -320,12 +326,13 @@ int i2c_probe(uchar chip) * cmd_eeprom, so we have to choose here, and for the moment that'll be * a repeated start without a preceding stop. */ -int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *data, int length) { int status; /* begin i2c write to send the address bytes */ - status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + status = i2c_begin(MVTWSI_STATUS_START, (chip << 1)); /* send addr bytes */ while ((status == 0) && alen--) status = twsi_send(addr >> (8*alen), @@ -333,7 +340,7 @@ int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) /* begin i2c read to receive eeprom data bytes */ if (status == 0) status = i2c_begin( - MVTWSI_STATUS_REPEATED_START, (dev << 1) | 1); + MVTWSI_STATUS_REPEATED_START, (chip << 1) | 1); /* prepare ACK if at least one byte must be received */ if (length > 0) twsi_control_flags |= MVTWSI_CONTROL_ACK; @@ -355,12 +362,13 @@ int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c * Begin write, send address byte(s), send data bytes, end. */ -int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *data, int length) { int status; /* begin i2c write to send the eeprom adress bytes then data bytes */ - status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + status = i2c_begin(MVTWSI_STATUS_START, (chip << 1)); /* send addr bytes */ while ((status == 0) && alen--) status = twsi_send(addr >> (8*alen), @@ -374,21 +382,7 @@ int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) return status; } -/* - * Bus set routine: we only support bus 0. - */ -int i2c_set_bus_num(unsigned int bus) -{ - if (bus > 0) { - return -1; - } - return 0; -} - -/* - * Bus get routine: hard-return bus 0. - */ -unsigned int i2c_get_bus_num(void) -{ - return 0; -} +U_BOOT_I2C_ADAP_COMPLETE(twsi0, twsi_i2c_init, twsi_i2c_probe, + twsi_i2c_read, twsi_i2c_write, + twsi_i2c_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 0) -- cgit v1.1 From 6620377e4b8be3c232c59d673efcd673c30bc69f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Jun 2014 22:55:49 +0200 Subject: sunxi: Add i2c support Add support for the i2c controller found on all Allwinner sunxi SoCs, this is the same controller as found on the Marvell orion5x and kirkwood SoC families, with a slightly different register layout, so this patch uses the existing mvtwsi code. Signed-off-by: Hans de Goede Acked-by: Ian Campbell Acked-By: Prafulla Wadaskar Acked-by: Heiko Schocher [ ijc -- updated u-boot-spl-fel.lds ] --- drivers/i2c/mvtwsi.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index c8b5425..ab3ffa0 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -22,6 +22,8 @@ #include #elif defined(CONFIG_KIRKWOOD) #include +#elif defined(CONFIG_SUNXI) +#include #else #error Driver mvtwsi not supported by SoC or board #endif @@ -30,6 +32,20 @@ * TWSI register structure */ +#ifdef CONFIG_SUNXI + +struct mvtwsi_registers { + u32 slave_address; + u32 xtnd_slave_addr; + u32 data; + u32 control; + u32 status; + u32 baudrate; + u32 soft_reset; +}; + +#else + struct mvtwsi_registers { u32 slave_address; u32 data; @@ -43,6 +59,8 @@ struct mvtwsi_registers { u32 soft_reset; }; +#endif + /* * Control register fields */ -- cgit v1.1 From 14bc66bd9a1d8073a12c6e785ab379909f620356 Mon Sep 17 00:00:00 2001 From: Henrik Nordstrom Date: Fri, 13 Jun 2014 22:55:50 +0200 Subject: sunxi: Add axp209 pmic support Add support for the x-powers axp209 pmic which is found on most A10, A13 and A20 boards. And enable AXP209 support for the Cubietruck and Cubieboard boards. Signed-off-by: Henrik Nordstrom Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- drivers/power/Makefile | 1 + drivers/power/axp209.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 drivers/power/axp209.c (limited to 'drivers') diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 53ff97d..063ac8f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c new file mode 100644 index 0000000..9798e5b --- /dev/null +++ b/drivers/power/axp209.c @@ -0,0 +1,167 @@ +/* + * (C) Copyright 2012 + * Henrik Nordstrom + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +enum axp209_reg { + AXP209_POWER_STATUS = 0x00, + AXP209_CHIP_VERSION = 0x03, + AXP209_DCDC2_VOLTAGE = 0x23, + AXP209_DCDC3_VOLTAGE = 0x27, + AXP209_LDO24_VOLTAGE = 0x28, + AXP209_LDO3_VOLTAGE = 0x29, + AXP209_IRQ_STATUS5 = 0x4c, + AXP209_SHUTDOWN = 0x32, +}; + +#define AXP209_POWER_STATUS_ON_BY_DC (1 << 0) + +#define AXP209_IRQ5_PEK_UP (1 << 6) +#define AXP209_IRQ5_PEK_DOWN (1 << 5) + +#define AXP209_POWEROFF (1 << 7) + +static int axp209_write(enum axp209_reg reg, u8 val) +{ + return i2c_write(0x34, reg, 1, &val, 1); +} + +static int axp209_read(enum axp209_reg reg, u8 *val) +{ + return i2c_read(0x34, reg, 1, val, 1); +} + +static u8 axp209_mvolt_to_cfg(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +int axp209_set_dcdc2(int mvolt) +{ + int rc; + u8 cfg, current; + + cfg = axp209_mvolt_to_cfg(mvolt, 700, 2275, 25); + + /* Do we really need to be this gentle? It has built-in voltage slope */ + while ((rc = axp209_read(AXP209_DCDC2_VOLTAGE, ¤t)) == 0 && + current != cfg) { + if (current < cfg) + current++; + else + current--; + + rc = axp209_write(AXP209_DCDC2_VOLTAGE, current); + if (rc) + break; + } + + return rc; +} + +int axp209_set_dcdc3(int mvolt) +{ + u8 cfg = axp209_mvolt_to_cfg(mvolt, 700, 3500, 25); + + return axp209_write(AXP209_DCDC3_VOLTAGE, cfg); +} + +int axp209_set_ldo2(int mvolt) +{ + int rc; + u8 cfg, reg; + + cfg = axp209_mvolt_to_cfg(mvolt, 1800, 3300, 100); + + rc = axp209_read(AXP209_LDO24_VOLTAGE, ®); + if (rc) + return rc; + + /* LDO2 configuration is in upper 4 bits */ + reg = (reg & 0x0f) | (cfg << 4); + return axp209_write(AXP209_LDO24_VOLTAGE, reg); +} + +int axp209_set_ldo3(int mvolt) +{ + u8 cfg; + + if (mvolt == -1) + cfg = 0x80; /* determined by LDO3IN pin */ + else + cfg = axp209_mvolt_to_cfg(mvolt, 700, 2275, 25); + + return axp209_write(AXP209_LDO3_VOLTAGE, cfg); +} + +int axp209_set_ldo4(int mvolt) +{ + int rc; + static const int vindex[] = { + 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2500, + 2700, 2800, 3000, 3100, 3200, 3300 + }; + u8 cfg, reg; + + /* Translate mvolt to register cfg value, requested <= selected */ + for (cfg = 15; vindex[cfg] > mvolt && cfg > 0; cfg--); + + rc = axp209_read(AXP209_LDO24_VOLTAGE, ®); + if (rc) + return rc; + + /* LDO4 configuration is in lower 4 bits */ + reg = (reg & 0xf0) | (cfg << 0); + return axp209_write(AXP209_LDO24_VOLTAGE, reg); +} + +int axp209_init(void) +{ + u8 ver; + int rc; + + rc = axp209_read(AXP209_CHIP_VERSION, &ver); + if (rc) + return rc; + + /* Low 4 bits is chip version */ + ver &= 0x0f; + + if (ver != 0x1) + return -1; + + return 0; +} + +int axp209_poweron_by_dc(void) +{ + u8 v; + + if (axp209_read(AXP209_POWER_STATUS, &v)) + return 0; + + return (v & AXP209_POWER_STATUS_ON_BY_DC); +} + +int axp209_power_button(void) +{ + u8 v; + + if (axp209_read(AXP209_IRQ_STATUS5, &v)) + return 0; + + axp209_write(AXP209_IRQ_STATUS5, AXP209_IRQ5_PEK_DOWN); + + return v & AXP209_IRQ5_PEK_DOWN; +} -- cgit v1.1 From 24289208354c143967968755cff5c825a12e3f90 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Jun 2014 22:55:51 +0200 Subject: sunxi: Add axp152 pmic support Add support for the x-powers axp152 pmic which is found on most A10s boards and enable it for the r7-tv-dongle board. Signed-off-by: Henrik Nordstrom Signed-off-by: Ian Campbell Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- drivers/power/Makefile | 1 + drivers/power/axp152.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 drivers/power/axp152.c (limited to 'drivers') diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 063ac8f..dc64e4d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o diff --git a/drivers/power/axp152.c b/drivers/power/axp152.c new file mode 100644 index 0000000..fa4ea05 --- /dev/null +++ b/drivers/power/axp152.c @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2012 + * Henrik Nordstrom + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +enum axp152_reg { + AXP152_CHIP_VERSION = 0x3, + AXP152_DCDC2_VOLTAGE = 0x23, + AXP152_DCDC3_VOLTAGE = 0x27, + AXP152_DCDC4_VOLTAGE = 0x2B, + AXP152_LDO2_VOLTAGE = 0x2A, + AXP152_SHUTDOWN = 0x32, +}; + +#define AXP152_POWEROFF (1 << 7) + +static int axp152_write(enum axp152_reg reg, u8 val) +{ + return i2c_write(0x30, reg, 1, &val, 1); +} + +static int axp152_read(enum axp152_reg reg, u8 *val) +{ + return i2c_read(0x30, reg, 1, val, 1); +} + +static u8 axp152_mvolt_to_target(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +int axp152_set_dcdc2(int mvolt) +{ + int rc; + u8 current, target; + + target = axp152_mvolt_to_target(mvolt, 700, 2275, 25); + + /* Do we really need to be this gentle? It has built-in voltage slope */ + while ((rc = axp152_read(AXP152_DCDC2_VOLTAGE, ¤t)) == 0 && + current != target) { + if (current < target) + current++; + else + current--; + rc = axp152_write(AXP152_DCDC2_VOLTAGE, current); + if (rc) + break; + } + return rc; +} + +int axp152_set_dcdc3(int mvolt) +{ + u8 target = axp152_mvolt_to_target(mvolt, 700, 3500, 25); + + return axp152_write(AXP152_DCDC3_VOLTAGE, target); +} + +int axp152_set_dcdc4(int mvolt) +{ + u8 target = axp152_mvolt_to_target(mvolt, 700, 3500, 25); + + return axp152_write(AXP152_DCDC4_VOLTAGE, target); +} + +int axp152_set_ldo2(int mvolt) +{ + u8 target = axp152_mvolt_to_target(mvolt, 700, 3500, 100); + + return axp152_write(AXP152_LDO2_VOLTAGE, target); +} + +int axp152_init(void) +{ + u8 ver; + int rc; + + rc = axp152_read(AXP152_CHIP_VERSION, &ver); + if (rc) + return rc; + + if (ver != 0x05) + return -1; + + return 0; +} -- cgit v1.1 From abce2c6220c1f8f4b66e464adc1074e04a8f19eb Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 5 Jun 2014 19:00:15 +0100 Subject: sunxi: add gpio driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch enables CONFIG_CMD_GPIO for the Allwinner (sunxi) platform as well as providing the common gpio API (gpio_request/free, direction in/out, get/set etc). Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Ma Haijun Signed-off-by: Oliver Schinagl Signed-off-by: Ian Campbell Cc: Henrik Nordström Cc: Tom Cubie Acked-by: Hans de Goede --- drivers/gpio/Makefile | 1 + drivers/gpio/sunxi_gpio.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 drivers/gpio/sunxi_gpio.c (limited to 'drivers') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4e001e1..86813b9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -34,3 +34,4 @@ obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o obj-$(CONFIG_ADI_GPIO2) += adi_gpio2.o obj-$(CONFIG_TCA642X) += tca642x.o oby-$(CONFIG_SX151X) += sx151x.o +obj-$(CONFIG_SUNXI_GPIO) += sunxi_gpio.o diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c new file mode 100644 index 0000000..0c50a8f --- /dev/null +++ b/drivers/gpio/sunxi_gpio.c @@ -0,0 +1,102 @@ +/* + * (C) Copyright 2012 Henrik Nordstrom + * + * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c: + * + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. + * Tom Cubie + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static int sunxi_gpio_output(u32 pin, u32 val) +{ + u32 dat; + u32 bank = GPIO_BANK(pin); + u32 num = GPIO_NUM(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + dat = readl(&pio->dat); + if (val) + dat |= 0x1 << num; + else + dat &= ~(0x1 << num); + + writel(dat, &pio->dat); + + return 0; +} + +static int sunxi_gpio_input(u32 pin) +{ + u32 dat; + u32 bank = GPIO_BANK(pin); + u32 num = GPIO_NUM(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + dat = readl(&pio->dat); + dat >>= num; + + return dat & 0x1; +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); + + return sunxi_gpio_input(gpio); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); + + return sunxi_gpio_output(gpio, value); +} + +int gpio_get_value(unsigned gpio) +{ + return sunxi_gpio_input(gpio); +} + +int gpio_set_value(unsigned gpio, int value) +{ + return sunxi_gpio_output(gpio, value); +} + +int sunxi_name_to_gpio(const char *name) +{ + int group = 0; + int groupsize = 9 * 32; + long pin; + char *eptr; + if (*name == 'P' || *name == 'p') + name++; + if (*name >= 'A') { + group = *name - (*name > 'a' ? 'a' : 'A'); + groupsize = 32; + name++; + } + + pin = simple_strtol(name, &eptr, 10); + if (!*name || *eptr) + return -1; + if (pin < 0 || pin > groupsize || group >= 9) + return -1; + return group * 32 + pin; +} -- cgit v1.1 From c15df21fe79d420344a3ecb1bb60b8f97c6dec2e Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Tue, 17 Jun 2014 22:47:31 +0200 Subject: mtd: cfi_flash: fix clang warning clang warns this check is silly; it is since s is a local variable. u-boot/drivers/mtd/cfi_flash.c:2363:13: warning: comparison of array 's' not equal to a null pointer is always true else if ((s != NULL) && (strcmp(s, "yes") == 0)) { cc: Stefan Roese Signed-off-by: Jeroen Hofstee --- drivers/mtd/cfi_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index a389cd1..c4b5bc1 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -2360,7 +2360,7 @@ unsigned long flash_init (void) #endif /* CONFIG_SYS_FLASH_QUIET_TEST */ } #ifdef CONFIG_SYS_FLASH_PROTECTION - else if ((s != NULL) && (strcmp(s, "yes") == 0)) { + else if (strcmp(s, "yes") == 0) { /* * Only the U-Boot image and it's environment * is protected, all other sectors are -- cgit v1.1 From fd536d818d9bb9138ca74dedf187b8086eb08a24 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Wed, 25 Jun 2014 21:57:45 +0200 Subject: dm: add missing includes lists.c / root.c do not include their own header and they could potentially implement a different function. Therefore actually include the headers. cc: sjg@chromium.org Signed-off-by: Jeroen Hofstee Acked-by: Simon Glass --- drivers/core/lists.c | 1 + drivers/core/root.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/core/lists.c b/drivers/core/lists.c index afb59d1..87164a5 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/core/root.c b/drivers/core/root.c index 1cbb096..11e0879 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include -- cgit v1.1 From cee9ab7cb62ee3cb5544ce2a217644a166340f93 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Thu, 10 Jul 2014 22:46:28 +0200 Subject: dirvers: mmc: use __weak use weak instead of alias to prevent some clang warnings. Signed-off-by: Jeroen Hofstee --- drivers/mmc/mmc.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b5477b1..e3df1f7 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -42,13 +42,11 @@ int mmc_getwp(struct mmc *mmc) return wp; } -int __board_mmc_getcd(struct mmc *mmc) { +__weak int board_mmc_getcd(struct mmc *mmc) +{ return -1; } -int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, - alias("__board_mmc_getcd"))); - int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { int ret; @@ -1371,17 +1369,17 @@ int mmc_set_dsr(struct mmc *mmc, u16 val) return 0; } -/* - * CPU and board-specific MMC initializations. Aliased function - * signals caller to move on - */ -static int __def_mmc_init(bd_t *bis) +/* CPU-specific MMC initializations */ +__weak int cpu_mmc_init(bd_t *bis) { return -1; } -int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); -int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +/* board-specific MMC initializations. */ +__weak int board_mmc_init(bd_t *bis) +{ + return -1; +} #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) -- cgit v1.1 From 750121c3506399e758849a4f37c772c3f317045f Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Sat, 12 Jul 2014 21:24:08 +0200 Subject: mmc: prevent some warnings with make W=1 Add missing prototypes for global functions and make local functions static. cc: panto@antoniou-consulting.com Signed-off-by: Jeroen Hofstee --- drivers/mmc/mmc.c | 6 +++--- drivers/mmc/omap_hsmmc.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e3df1f7..a26f3ce 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -21,7 +21,7 @@ static struct list_head mmc_devices; static int cur_dev_num = -1; -int __weak board_mmc_getwp(struct mmc *mmc) +__weak int board_mmc_getwp(struct mmc *mmc) { return -1; } @@ -375,7 +375,7 @@ static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, return 0; } -int mmc_send_op_cond(struct mmc *mmc) +static int mmc_send_op_cond(struct mmc *mmc) { struct mmc_cmd cmd; int err, i; @@ -397,7 +397,7 @@ int mmc_send_op_cond(struct mmc *mmc) return IN_PROGRESS; } -int mmc_complete_op_cond(struct mmc *mmc) +static int mmc_complete_op_cond(struct mmc *mmc) { struct mmc_cmd cmd; int timeout = 1000; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 17cbb09..5b0c302 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -120,7 +120,7 @@ static void omap5_pbias_config(struct mmc *mmc) } #endif -unsigned char mmc_board_init(struct mmc *mmc) +static unsigned char mmc_board_init(struct mmc *mmc) { #if defined(CONFIG_OMAP34XX) t2_t *t2_base = (t2_t *)T2_BASE; -- cgit v1.1 From 4a755f1da52f2baee68888e21599dc8b80c89033 Mon Sep 17 00:00:00 2001 From: Lijun Pan Date: Fri, 20 Jun 2014 12:18:39 -0500 Subject: driver/usb: include upper/lower_32_bits() from linux/compat.h upper_32_bits() and lower_32_bits() have been ported into linux/compat.h. Start use them now in drivers/usb/host/xhci.h. Signed-off-by: Lijun Pan --- drivers/usb/host/xhci.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ceb1573..6685ed2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -20,9 +20,7 @@ #include #include #include - -#define upper_32_bits(n) (u32)((n) >> 32) -#define lower_32_bits(n) (u32)(n) +#include #define MAX_EP_CTX_NUM 31 #define XHCI_ALIGNMENT 64 @@ -1121,7 +1119,7 @@ static inline void xhci_writeq(__le64 volatile *regs, const u64 val) __u32 *ptr = (__u32 *)regs; u32 val_lo = lower_32_bits(val); /* FIXME */ - u32 val_hi = 0; + u32 val_hi = upper_32_bits(val); writel(val_lo, ptr); writel(val_hi, ptr + 1); } -- cgit v1.1 From 841977df21e5f2c1276ae5a33cca3ac4fb976ad9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 11 Jul 2014 20:29:02 +0900 Subject: serial: ns16550: drop CONFIG_OMAP1610 from the special case If CONFIG_OMAP1610 is defined, the code returning the fixed value (26) is enabled. But this case is covered by the following code. (CONFIG_SYS_NS16550_CLK + (gd->baudrate * (MODE_X_DIV / 2))) / (MODE_X_DIV * gd->baudrate) = (48000000 + (115200 * (16 / 2))) / (16 * 115200) = 48921600 / 1843200 = 26 The "#ifdef CONFIG_OMAP1610" was added by commit 6f21347d more than ten years ago. In those days, the divide-and-round was not used. I guess that is why this weird code was added here. Signed-off-by: Masahiro Yamada Cc: Tom Rini Cc: Rishi Bhattacharya --- drivers/serial/serial_ns16550.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index ba68d46..056ef2a 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -128,12 +128,6 @@ static int calc_divisor (NS16550_t port) } port->osc_12m_sel = 0; /* clear if previsouly set */ #endif -#ifdef CONFIG_OMAP1610 - /* If can't cleanly clock 115200 set div to 1 */ - if ((CONFIG_SYS_NS16550_CLK == 48000000) && (gd->baudrate == 115200)) { - return (26); /* return 26 for base divisor */ - } -#endif #define MODE_X_DIV 16 /* Compute divisor value. Normally, we should simply return: -- cgit v1.1 From f8c7c2033df1f8aa1cd329292f01e87972d84535 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 11 Jul 2014 20:29:03 +0900 Subject: serial: ns16550: use DIV_ROUND_CLOSEST macro to compute the divisor The function still returns the same value. The comment block is no longer necessary because our intention is clear enough by using DIV_ROUND_CLOSEST() macro. Signed-off-by: Masahiro Yamada --- drivers/serial/serial_ns16550.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 056ef2a..49e2c1f 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -130,13 +130,9 @@ static int calc_divisor (NS16550_t port) #endif #define MODE_X_DIV 16 - /* Compute divisor value. Normally, we should simply return: - * CONFIG_SYS_NS16550_CLK) / MODE_X_DIV / gd->baudrate - * but we need to round that value by adding 0.5. - * Rounding is especially important at high baud rates. - */ - return (CONFIG_SYS_NS16550_CLK + (gd->baudrate * (MODE_X_DIV / 2))) / - (MODE_X_DIV * gd->baudrate); + + return DIV_ROUND_CLOSEST(CONFIG_SYS_NS16550_CLK, + MODE_X_DIV * gd->baudrate); } void -- cgit v1.1 From 5b9587ae31e26c5d6fd04ff0ef8cd7541b5abf56 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 11 Jul 2014 20:29:04 +0900 Subject: serial: ns16550: use a const variable instead of macro Just for type checking. Signed-off-by: Masahiro Yamada Cc: Marek Vasut Acked-by: Marek Vasut --- drivers/serial/serial_ns16550.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 49e2c1f..4413e69 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -120,6 +120,8 @@ static NS16550_t serial_ports[6] = { static int calc_divisor (NS16550_t port) { + const unsigned int mode_x_div = 16; + #ifdef CONFIG_OMAP1510 /* If can't cleanly clock 115200 set div to 1 */ if ((CONFIG_SYS_NS16550_CLK == 12000000) && (gd->baudrate == 115200)) { @@ -129,10 +131,8 @@ static int calc_divisor (NS16550_t port) port->osc_12m_sel = 0; /* clear if previsouly set */ #endif -#define MODE_X_DIV 16 - return DIV_ROUND_CLOSEST(CONFIG_SYS_NS16550_CLK, - MODE_X_DIV * gd->baudrate); + mode_x_div * gd->baudrate); } void -- cgit v1.1 From e0ddcf93b93eb0bcd99501f90fd62e91c864e114 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 18 Jul 2014 20:38:39 +0100 Subject: AHCI: Increase link timeout to 200ms In 73545f75b66d "ahci: wait longer for link" I increased the timeout to 40ms based on the observed behaviour of a WD disk on a Cubietruck. Since then Karsten Merker and myself have both observed timeouts with HGST disks (Karsten on Cubietruck, me on Cubieboard2). Increasing the timeout to ~175ms fixes this, so go to 200ms for a bit of headroom. Signed-off-by: Ian Campbell Cc: Karsten Merker Acked-by: Hans de Goede --- drivers/block/ahci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index c8f6573..4df8046 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -41,7 +41,7 @@ u16 *ataid[AHCI_MAX_PORTS]; #define WAIT_MS_SPINUP 20000 #define WAIT_MS_DATAIO 5000 #define WAIT_MS_FLUSH 5000 -#define WAIT_MS_LINKUP 40 +#define WAIT_MS_LINKUP 200 static inline u32 ahci_port_base(u32 base, u32 port) { -- cgit v1.1 From 5b9765c7d689166899918d49e4884e9b8c51a8ae Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:54:56 -0600 Subject: dm: gpio: Don't use the driver model uclass for SPL Driver model does not support SPL yet, so we should not use the GPIO uclass for SPL. Signed-off-by: Simon Glass --- drivers/gpio/Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4e001e1..fb8dcd9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # +ifndef CONFIG_SPL_BUILD obj-$(CONFIG_DM_GPIO) += gpio-uclass.o +endif obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o -- cgit v1.1 From 91d0be1dd845913ba276e041dc11d1297390de11 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:54:58 -0600 Subject: stdio: Remove redundant code around stdio_register() calls There is no point in setting a structure's memory to NULL when it has already been zeroed with memset(). Also, there is no need to create a stub function for stdio to call - if the function is NULL it will not be called. This is a clean-up, with no change in functionality. Signed-off-by: Simon Glass Acked-by: Marek Vasut --- drivers/input/keyboard.c | 2 -- drivers/video/cfb_console.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard.c b/drivers/input/keyboard.c index 614592e..5ef1cc0 100644 --- a/drivers/input/keyboard.c +++ b/drivers/input/keyboard.c @@ -275,8 +275,6 @@ int kbd_init (void) memset (&kbddev, 0, sizeof(kbddev)); strcpy(kbddev.name, DEVNAME); kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; - kbddev.putc = NULL ; - kbddev.puts = NULL ; kbddev.getc = kbd_getc ; kbddev.tstc = kbd_testc ; diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index b52e9ed..1cf8660 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -2279,8 +2279,6 @@ int drv_video_init(void) console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; console_dev.putc = video_putc; /* 'putc' function */ console_dev.puts = video_puts; /* 'puts' function */ - console_dev.tstc = NULL; /* 'tstc' function */ - console_dev.getc = NULL; /* 'getc' function */ #if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) /* Also init console device */ -- cgit v1.1 From 709ea543b92489e7729d2d7ddd6c9f451e52158c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:54:59 -0600 Subject: stdio: Pass device pointer to stdio methods At present stdio device functions do not get any clue as to which stdio device is being acted on. Some implementations go to great lengths to work around this, such as defining a whole separate set of functions for each possible device. For driver model we need to associate a stdio_dev with a device. It doesn't seem possible to continue with this work-around approach. Instead, add a stdio_dev pointer to each of the stdio member functions. Note: The serial drivers have the same problem, but it is not strictly necessary to fix that to get driver model running. Also, if we convert serial over to driver model the problem will go away. Code size increases by 244 bytes for Thumb2 and 428 for PowerPC. 22: stdio: Pass device pointer to stdio methods arm: (for 2/2 boards) all +244.0 bss -4.0 text +248.0 powerpc: (for 1/1 boards) all +428.0 text +428.0 Signed-off-by: Simon Glass Acked-by: Marek Vasut Reviewed-by: Marek Vasut --- drivers/input/cros_ec_keyb.c | 6 ++--- drivers/input/i8042.c | 4 ++-- drivers/input/keyboard.c | 4 ++-- drivers/input/tegra-kbc.c | 6 ++--- drivers/misc/cbmem_console.c | 6 ++--- drivers/net/netconsole.c | 10 ++++---- drivers/serial/serial.c | 54 +++++++++++++++++++++++++++++++++++++++----- drivers/serial/usbtty.c | 8 +++---- drivers/video/cfb_console.c | 6 ++--- 9 files changed, 73 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/input/cros_ec_keyb.c b/drivers/input/cros_ec_keyb.c index a2501e0..47502b1 100644 --- a/drivers/input/cros_ec_keyb.c +++ b/drivers/input/cros_ec_keyb.c @@ -93,7 +93,7 @@ static int check_for_keys(struct keyb *config, * * @return 0 if no keys available, 1 if keys are available */ -static int kbd_tstc(void) +static int kbd_tstc(struct stdio_dev *dev) { /* Just get input to do this for us */ return config.inited ? input_tstc(&config.input) : 0; @@ -104,7 +104,7 @@ static int kbd_tstc(void) * * @return ASCII key code, or 0 if no key, or -1 if error */ -static int kbd_getc(void) +static int kbd_getc(struct stdio_dev *dev) { /* Just get input to do this for us */ return config.inited ? input_getc(&config.input) : 0; @@ -214,7 +214,7 @@ static int cros_ec_keyb_decode_fdt(const void *blob, int node, * * @return 0 if ok, -1 on error */ -static int cros_ec_init_keyboard(void) +static int cros_ec_init_keyboard(struct stdio_dev *dev) { const void *blob = gd->fdt_blob; int node; diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 35fa0bb..ca1604c 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -398,7 +398,7 @@ int i8042_kbd_init(void) * i8042_tstc - test if keyboard input is available * option: cursor blinking if called in a loop */ -int i8042_tstc(void) +int i8042_tstc(struct stdio_dev *dev) { unsigned char scan_code = 0; @@ -432,7 +432,7 @@ int i8042_tstc(void) * i8042_getc - wait till keyboard input is available * option: turn on/off cursor while waiting */ -int i8042_getc(void) +int i8042_getc(struct stdio_dev *dev) { int ret_chr; unsigned char scan_code; diff --git a/drivers/input/keyboard.c b/drivers/input/keyboard.c index 5ef1cc0..be0f333 100644 --- a/drivers/input/keyboard.c +++ b/drivers/input/keyboard.c @@ -70,7 +70,7 @@ static void kbd_put_queue(char data) } /* test if a character is in the queue */ -static int kbd_testc(void) +static int kbd_testc(struct stdio_dev *dev) { #if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) /* no ISR is used, so received chars must be polled */ @@ -83,7 +83,7 @@ static int kbd_testc(void) } /* gets the character from the queue */ -static int kbd_getc(void) +static int kbd_getc(struct stdio_dev *dev) { char c; while(in_pointer==out_pointer) { diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c index f137f93..7e36db0 100644 --- a/drivers/input/tegra-kbc.c +++ b/drivers/input/tegra-kbc.c @@ -194,7 +194,7 @@ int tegra_kbc_check(struct input_config *input) * * @return 0 if no keys available, 1 if keys are available */ -static int kbd_tstc(void) +static int kbd_tstc(struct stdio_dev *dev) { /* Just get input to do this for us */ return input_tstc(&config.input); @@ -207,7 +207,7 @@ static int kbd_tstc(void) * * @return ASCII key code, or 0 if no key, or -1 if error */ -static int kbd_getc(void) +static int kbd_getc(struct stdio_dev *dev) { /* Just get input to do this for us */ return input_getc(&config.input); @@ -289,7 +289,7 @@ static void tegra_kbc_open(void) * * @return 0 if ok, -ve on error */ -static int init_tegra_keyboard(void) +static int init_tegra_keyboard(struct stdio_dev *dev) { /* check if already created */ if (config.created) diff --git a/drivers/misc/cbmem_console.c b/drivers/misc/cbmem_console.c index 80a84fd..5f85ccf 100644 --- a/drivers/misc/cbmem_console.c +++ b/drivers/misc/cbmem_console.c @@ -31,7 +31,7 @@ struct cbmem_console { static struct cbmem_console *cbmem_console_p; -void cbmemc_putc(char data) +void cbmemc_putc(struct stdio_dev *dev, char data) { int cursor; @@ -40,12 +40,12 @@ void cbmemc_putc(char data) cbmem_console_p->buffer_body[cursor] = data; } -void cbmemc_puts(const char *str) +void cbmemc_puts(struct stdio_dev *dev, const char *str) { char c; while ((c = *str++) != 0) - cbmemc_putc(c); + cbmemc_putc(dev, c); } int cbmemc_init(void) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 65c747e..623f749 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -215,7 +215,7 @@ static void nc_send_packet(const char *buf, int len) } } -static int nc_start(void) +static int nc_start(struct stdio_dev *dev) { int retval; @@ -235,7 +235,7 @@ static int nc_start(void) return 0; } -static void nc_putc(char c) +static void nc_putc(struct stdio_dev *dev, char c) { if (output_recursion) return; @@ -246,7 +246,7 @@ static void nc_putc(char c) output_recursion = 0; } -static void nc_puts(const char *s) +static void nc_puts(struct stdio_dev *dev, const char *s) { int len; @@ -265,7 +265,7 @@ static void nc_puts(const char *s) output_recursion = 0; } -static int nc_getc(void) +static int nc_getc(struct stdio_dev *dev) { uchar c; @@ -286,7 +286,7 @@ static int nc_getc(void) return c; } -static int nc_tstc(void) +static int nc_tstc(struct stdio_dev *dev) { struct eth_device *eth; diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index fd61a5e..803d850 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -254,6 +254,48 @@ void serial_initialize(void) serial_assign(default_serial_console()->name); } +int serial_stub_start(struct stdio_dev *sdev) +{ + struct serial_device *dev = sdev->priv; + + return dev->start(); +} + +int serial_stub_stop(struct stdio_dev *sdev) +{ + struct serial_device *dev = sdev->priv; + + return dev->stop(); +} + +void serial_stub_putc(struct stdio_dev *sdev, const char ch) +{ + struct serial_device *dev = sdev->priv; + + dev->putc(ch); +} + +void serial_stub_puts(struct stdio_dev *sdev, const char *str) +{ + struct serial_device *dev = sdev->priv; + + dev->puts(str); +} + +int serial_stub_getc(struct stdio_dev *sdev) +{ + struct serial_device *dev = sdev->priv; + + return dev->getc(); +} + +int serial_stub_tstc(struct stdio_dev *sdev) +{ + struct serial_device *dev = sdev->priv; + + return dev->tstc(); +} + /** * serial_stdio_init() - Register serial ports with STDIO core * @@ -272,12 +314,12 @@ void serial_stdio_init(void) strcpy(dev.name, s->name); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; - dev.start = s->start; - dev.stop = s->stop; - dev.putc = s->putc; - dev.puts = s->puts; - dev.getc = s->getc; - dev.tstc = s->tstc; + dev.start = serial_stub_start; + dev.stop = serial_stub_stop; + dev.putc = serial_stub_putc; + dev.puts = serial_stub_puts; + dev.getc = serial_stub_getc; + dev.tstc = serial_stub_tstc; stdio_register(&dev); diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index 6b912ef..b030526 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -389,7 +389,7 @@ static void str2wide (char *str, u16 * wide) * Test whether a character is in the RX buffer */ -int usbtty_tstc (void) +int usbtty_tstc(struct stdio_dev *dev) { struct usb_endpoint_instance *endpoint = &endpoint_instance[rx_endpoint]; @@ -409,7 +409,7 @@ int usbtty_tstc (void) * written into its argument c. */ -int usbtty_getc (void) +int usbtty_getc(struct stdio_dev *dev) { char c; struct usb_endpoint_instance *endpoint = @@ -429,7 +429,7 @@ int usbtty_getc (void) /* * Output a single byte to the usb client port. */ -void usbtty_putc (const char c) +void usbtty_putc(struct stdio_dev *dev, const char c) { if (!usbtty_configured ()) return; @@ -484,7 +484,7 @@ static void __usbtty_puts (const char *str, int len) } } -void usbtty_puts (const char *str) +void usbtty_puts(struct stdio_dev *dev, const char *str) { int n; int len; diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 1cf8660..9231927 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -944,7 +944,7 @@ static void parse_putc(const char c) CURSOR_SET; } -void video_putc(const char c) +void video_putc(struct stdio_dev *dev, const char c) { #ifdef CONFIG_CFB_CONSOLE_ANSI int i; @@ -1158,12 +1158,12 @@ void video_putc(const char c) flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); } -void video_puts(const char *s) +void video_puts(struct stdio_dev *dev, const char *s) { int count = strlen(s); while (count--) - video_putc(*s++); + video_putc(dev, *s++); } /* -- cgit v1.1 From 7497812d47d4ad17172ec373469a33a6ab8b257e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:00 -0600 Subject: dm: Make sure that the root device is probed The root device should be probed just like any other device. The effect of this is to mark the device as activated, so that it can be removed (along with its children) if required. Signed-off-by: Simon Glass Acked-by: Marek Vasut --- drivers/core/root.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index 11e0879..ac1c164 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -49,6 +49,9 @@ int dm_init(void) ret = device_bind_by_name(NULL, &root_info, &DM_ROOT_NON_CONST); if (ret) return ret; + ret = device_probe(DM_ROOT_NON_CONST); + if (ret) + return ret; return 0; } -- cgit v1.1 From 9adbd7a116d62349eb0a85b5a08ab3ff0a12d556 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:01 -0600 Subject: dm: Provide a way to shut down driver model Add a new method which removes and unbinds all drivers. Signed-off-by: Simon Glass Acked-by: Marek Vasut --- drivers/core/root.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index ac1c164..346d462 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -56,6 +56,14 @@ int dm_init(void) return 0; } +int dm_uninit(void) +{ + device_remove(dm_root()); + device_unbind(dm_root()); + + return 0; +} + int dm_scan_platdata(void) { int ret; -- cgit v1.1 From 00606d7e39da4a8ecfbbc19d5af252bdfdd1fcc9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:03 -0600 Subject: dm: Allow drivers to be marked 'before relocation' Driver model currently only operates after relocation is complete. In this state U-Boot typically has a small amount of memory available. In adding support for driver model prior to relocation we must try to use as little memory as possible. In addition, on some machines the memory has not be inited and/or the CPU is not running at full speed or the data cache is off. These can reduce execution performance, so the less initialisation that is done before relocation the better. An immediately-obvious improvement is to only initialise drivers which are actually going to be used before relocation. On many boards the only such driver is a serial UART, so this provides a very large potential benefit. Allow drivers to mark themselves as 'pre-reloc' which means that they will be initialised prior to relocation. This can be done either with a driver flag or with a 'dm,pre-reloc' device tree property. To support this, the various dm scanning function now take a 'pre_reloc_only' parameter which indicates that only drivers marked pre-reloc should be bound. Signed-off-by: Simon Glass --- drivers/core/device.c | 6 ++++-- drivers/core/lists.c | 6 +++--- drivers/core/root.c | 11 +++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index c73c339..86b9ff8 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -129,14 +129,16 @@ fail_bind: return ret; } -int device_bind_by_name(struct udevice *parent, const struct driver_info *info, - struct udevice **devp) +int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, + const struct driver_info *info, struct udevice **devp) { struct driver *drv; drv = lists_driver_lookup_name(info->name); if (!drv) return -ENOENT; + if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) + return -EPERM; return device_bind(parent, drv, info->name, (void *)info->platdata, -1, devp); diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 87164a5..5f1c85f 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -62,7 +62,7 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id) return NULL; } -int lists_bind_drivers(struct udevice *parent) +int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) { struct driver_info *info = ll_entry_start(struct driver_info, driver_info); @@ -73,8 +73,8 @@ int lists_bind_drivers(struct udevice *parent) int ret; for (entry = info; entry != info + n_ents; entry++) { - ret = device_bind_by_name(parent, entry, &dev); - if (ret) { + ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev); + if (ret && ret != -EPERM) { dm_warn("No match for driver '%s'\n", entry->name); if (!result || ret != -ENOENT) result = ret; diff --git a/drivers/core/root.c b/drivers/core/root.c index 346d462..ce4eef3 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -46,7 +46,7 @@ int dm_init(void) } INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); - ret = device_bind_by_name(NULL, &root_info, &DM_ROOT_NON_CONST); + ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); if (ret) return ret; ret = device_probe(DM_ROOT_NON_CONST); @@ -64,11 +64,11 @@ int dm_uninit(void) return 0; } -int dm_scan_platdata(void) +int dm_scan_platdata(bool pre_reloc_only) { int ret; - ret = lists_bind_drivers(DM_ROOT_NON_CONST); + ret = lists_bind_drivers(DM_ROOT_NON_CONST, pre_reloc_only); if (ret == -ENOENT) { dm_warn("Some drivers were not found\n"); ret = 0; @@ -80,7 +80,7 @@ int dm_scan_platdata(void) } #ifdef CONFIG_OF_CONTROL -int dm_scan_fdt(const void *blob) +int dm_scan_fdt(const void *blob, bool pre_reloc_only) { int offset = 0; int ret = 0, err; @@ -89,6 +89,9 @@ int dm_scan_fdt(const void *blob) do { offset = fdt_next_node(blob, offset, &depth); if (offset > 0 && depth == 1) { + if (pre_reloc_only && + !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) + continue; err = lists_bind_fdt(gd->dm_root, blob, offset); if (err && !ret) ret = err; -- cgit v1.1 From ab7cd62790c4f7831b91eab8a2ec81742d01bb54 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:04 -0600 Subject: dm: Support driver model prior to relocation Initialise devices marked 'pre-reloc' and make them available prior to relocation. Note that this requires pre-reloc malloc() to be available. Signed-off-by: Simon Glass --- drivers/core/root.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index ce4eef3..6059756 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -105,6 +105,31 @@ int dm_scan_fdt(const void *blob, bool pre_reloc_only) } #endif +int dm_init_and_scan(bool pre_reloc_only) +{ + int ret; + + ret = dm_init(); + if (ret) { + debug("dm_init() failed: %d\n", ret); + return ret; + } + ret = dm_scan_platdata(pre_reloc_only); + if (ret) { + debug("dm_scan_platdata() failed: %d\n", ret); + return ret; + } +#ifdef CONFIG_OF_CONTROL + ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); + if (ret) { + debug("dm_scan_fdt() failed: %d\n", ret); + return ret; + } +#endif + + return 0; +} + /* This is the root driver - all drivers are children of this */ U_BOOT_DRIVER(root_driver) = { .name = "root_driver", -- cgit v1.1 From 093f79ab88d57b800283b0a172c17167699f4243 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:07 -0600 Subject: Add a flag indicating when the serial console is ready For sandbox we have a fallback console which is used very early in U-Boot, before serial drivers are available. Rather than try to guess when to switch to the real console, add a flag so we can be sure. This makes sure that sandbox can always output a panic() message, for example, and avoids silent failure (which is very annoying in sandbox). Signed-off-by: Simon Glass --- drivers/serial/serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 803d850..d2eb752 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -418,6 +418,7 @@ static struct serial_device *get_current(void) */ int serial_init(void) { + gd->flags |= GD_FLG_SERIAL_READY; return get_current()->start(); } -- cgit v1.1 From 9ca296a1b0ef37e53ca62ec8a345d9970f987599 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:08 -0600 Subject: dm: Move uclass error checking/probing into a function Several functions will use this same pattern, so bring it into a function. Signed-off-by: Simon Glass --- drivers/core/uclass.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 34723ec..db91526 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -158,13 +158,19 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) return -ENODEV; } -int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) +/** + * uclass_get_device_tail() - handle the end of a get_device call + * + * This handles returning an error or probing a device as needed. + * + * @dev: Device that needs to be probed + * @ret: Error to return. If non-zero then the device is not probed + * @devp: Returns the value of 'dev' if there is no error + * @return ret, if non-zero, else the result of the device_probe() call + */ +static int uclass_get_device_tail(struct udevice *dev, int ret, + struct udevice **devp) { - struct udevice *dev; - int ret; - - *devp = NULL; - ret = uclass_find_device(id, index, &dev); if (ret) return ret; @@ -177,6 +183,16 @@ int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) return 0; } +int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device(id, index, &dev); + return uclass_get_device_tail(dev, ret, devp); +} + int uclass_first_device(enum uclass_id id, struct udevice **devp) { struct uclass *uc; -- cgit v1.1 From 5a66a8ff86d923367ca9a1f6168e976fbde27391 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:12 -0600 Subject: dm: Introduce device sequence numbering In U-Boot it is pretty common to number devices from 0 and access them on the command line using this numbering. While it may come to pass that we will move away from this numbering, the possibility seems remote at present. Given that devices within a uclass will have an implied numbering, it makes sense to build this into driver model as a core feature. The cost is fairly small in terms of code and data space. With each uclass having numbered devices we can ask for SPI port 0 or serial port 1 and receive a single device. Devices typically request a sequence number using aliases in the device tree. These are resolved when the device is probed, to deal with conflicts. Sequence numbers need not be sequential and holes are permitted. At present there is no support for sequence numbers using static platform data. It could easily be added to 'struct driver_info' if needed, but it seems better to add features as we find a use for them, and the use of -1 to mean 'no sequence' makes the default value somewhat painful. Signed-off-by: Simon Glass --- drivers/core/device.c | 28 ++++++++++++++++++ drivers/core/uclass.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 86b9ff8..848ce3b 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -21,6 +22,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /** * device_chld_unbind() - Unbind all device's children from the device * @@ -95,6 +98,21 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name, dev->parent = parent; dev->driver = drv; dev->uclass = uc; + + /* + * For some devices, such as a SPI or I2C bus, the 'reg' property + * is a reasonable indicator of the sequence number. But if there is + * an alias, we use that in preference. In any case, this is just + * a 'requested' sequence, and will be resolved (and ->seq updated) + * when the device is probed. + */ + dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1); + dev->seq = -1; + if (uc->uc_drv->name && of_offset != -1) { + fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, + &dev->req_seq); + } + if (!dev->platdata && drv->platdata_auto_alloc_size) dev->flags |= DM_FLAG_ALLOC_PDATA; @@ -207,6 +225,7 @@ int device_probe(struct udevice *dev) struct driver *drv; int size = 0; int ret; + int seq; if (!dev) return -EINVAL; @@ -249,6 +268,13 @@ int device_probe(struct udevice *dev) goto fail; } + seq = uclass_resolve_seq(dev); + if (seq < 0) { + ret = seq; + goto fail; + } + dev->seq = seq; + if (drv->ofdata_to_platdata && dev->of_offset >= 0) { ret = drv->ofdata_to_platdata(dev); if (ret) @@ -276,6 +302,7 @@ fail_uclass: __func__, dev->name); } fail: + dev->seq = -1; device_free(dev); return ret; @@ -311,6 +338,7 @@ int device_remove(struct udevice *dev) device_free(dev); + dev->seq = -1; dev->flags &= ~DM_FLAG_ACTIVATED; return 0; diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index db91526..c28cf67 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -158,6 +158,35 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) return -ENODEV; } +int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, + bool find_req_seq, struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq); + if (seq_or_req_seq == -1) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + debug(" - %d %d\n", dev->req_seq, dev->seq); + if ((find_req_seq ? dev->req_seq : dev->seq) == + seq_or_req_seq) { + *devp = dev; + debug(" - found\n"); + return 0; + } + } + debug(" - not found\n"); + + return -ENODEV; +} + /** * uclass_get_device_tail() - handle the end of a get_device call * @@ -193,6 +222,23 @@ int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) return uclass_get_device_tail(dev, ret, devp); } +int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_seq(id, seq, false, &dev); + if (ret == -ENODEV) { + /* + * We didn't find it in probed devices. See if there is one + * that will request this seq if probed. + */ + ret = uclass_find_device_by_seq(id, seq, true, &dev); + } + return uclass_get_device_tail(dev, ret, devp); +} + int uclass_first_device(enum uclass_id id, struct udevice **devp) { struct uclass *uc; @@ -270,6 +316,37 @@ int uclass_unbind_device(struct udevice *dev) return 0; } +int uclass_resolve_seq(struct udevice *dev) +{ + struct udevice *dup; + int seq; + int ret; + + assert(dev->seq == -1); + ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq, + false, &dup); + if (!ret) { + dm_warn("Device '%s': seq %d is in use by '%s'\n", + dev->name, dev->req_seq, dup->name); + } else if (ret == -ENODEV) { + /* Our requested sequence number is available */ + if (dev->req_seq != -1) + return dev->req_seq; + } else { + return ret; + } + + for (seq = 0; seq < DM_MAX_SEQ; seq++) { + ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, seq, + false, &dup); + if (ret == -ENODEV) + break; + if (ret) + return ret; + } + return seq; +} + int uclass_post_probe_device(struct udevice *dev) { struct uclass_driver *uc_drv = dev->uclass->uc_drv; @@ -297,6 +374,7 @@ int uclass_pre_remove_device(struct udevice *dev) free(dev->uclass_priv); dev->uclass_priv = NULL; } + dev->seq = -1; return 0; } -- cgit v1.1 From f4cdead24a1a0c39c29c04e107c2f98ba61c5da8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:14 -0600 Subject: dm: Allow a device to be found by its FDT offset Each device that was bound from a device tree has an node that caused it to be bound. Add functions that find and return a device based on a device tree offset. Signed-off-by: Simon Glass --- drivers/core/uclass.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index c28cf67..a27f3d5 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -187,6 +187,30 @@ int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, return -ENODEV; } +static int uclass_find_device_by_of_offset(enum uclass_id id, int node, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + if (node < 0) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (dev->of_offset == node) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + /** * uclass_get_device_tail() - handle the end of a get_device call * @@ -239,6 +263,17 @@ int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp) return uclass_get_device_tail(dev, ret, devp); } +int uclass_get_device_by_of_offset(enum uclass_id id, int node, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_of_offset(id, node, &dev); + return uclass_get_device_tail(dev, ret, devp); +} + int uclass_first_device(enum uclass_id id, struct udevice **devp) { struct uclass *uc; -- cgit v1.1 From c910e2e2da49036496a5b8b34425043675218d51 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:15 -0600 Subject: dm: Avoid accessing uclasses before they are ready Don't allow access to uclasses before they have been initialised. Signed-off-by: Simon Glass --- drivers/core/uclass.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index a27f3d5..61ca17e 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -23,6 +23,8 @@ struct uclass *uclass_find(enum uclass_id key) { struct uclass *uc; + if (!gd->dm_root) + return NULL; /* * TODO(sjg@chromium.org): Optimise this, perhaps moving the found * node to the start of the list, or creating a linear array mapping -- cgit v1.1 From 1ca7e2062b4e8c3b211753dcb19c063b5b9b73ca Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:18 -0600 Subject: dm: Provide a function to scan child FDT nodes At present only root nodes in the device tree are scanned for devices. But some devices can have children. For example a SPI bus may have several children for each of its chip selects. Add a function which scans subnodes and binds devices for each one. This can be used for the root node scan also, so change it. A device can call this function in its bind() or probe() methods to bind its children. Signed-off-by: Simon Glass --- drivers/core/root.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index 6059756..4f9c7e7 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -80,29 +80,32 @@ int dm_scan_platdata(bool pre_reloc_only) } #ifdef CONFIG_OF_CONTROL -int dm_scan_fdt(const void *blob, bool pre_reloc_only) +int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, + bool pre_reloc_only) { - int offset = 0; int ret = 0, err; - int depth = 0; - - do { - offset = fdt_next_node(blob, offset, &depth); - if (offset > 0 && depth == 1) { - if (pre_reloc_only && - !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) - continue; - err = lists_bind_fdt(gd->dm_root, blob, offset); - if (err && !ret) - ret = err; - } - } while (offset > 0); + + for (offset = fdt_first_subnode(blob, offset); + offset > 0; + offset = fdt_next_subnode(blob, offset)) { + if (pre_reloc_only && + !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL)) + continue; + err = lists_bind_fdt(parent, blob, offset); + if (err && !ret) + ret = err; + } if (ret) dm_warn("Some drivers failed to bind\n"); return ret; } + +int dm_scan_fdt(const void *blob, bool pre_reloc_only) +{ + return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); +} #endif int dm_init_and_scan(bool pre_reloc_only) -- cgit v1.1 From 997c87bb0b1981fd33e34cefc26d9138f27326ce Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:19 -0600 Subject: dm: Add functions to access a device's children Devices can have childen that can be addressed by a simple index, the sequence number or a device tree offset. Add functions to access a child in each of these ways. The index is typically used as a fallback when the sequence number is not available. For example we may use a serial UART with sequence number 0 as the console, but if no UART has sequence number 0, then we can fall back to just using the first UART (index 0). The device tree offset function is useful for buses, where they want to locate one of their children. The device tree can be scanned to find the offset of each child, and that offset can then find the device. Signed-off-by: Simon Glass --- drivers/core/device.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 848ce3b..74bb5f0 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -376,3 +376,96 @@ void *dev_get_priv(struct udevice *dev) return dev->priv; } + +static int device_get_device_tail(struct udevice *dev, int ret, + struct udevice **devp) +{ + if (ret) + return ret; + + ret = device_probe(dev); + if (ret) + return ret; + + *devp = dev; + + return 0; +} + +int device_get_child(struct udevice *parent, int index, struct udevice **devp) +{ + struct udevice *dev; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (!index--) + return device_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq, + bool find_req_seq, struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + if (seq_or_req_seq == -1) + return -ENODEV; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if ((find_req_seq ? dev->req_seq : dev->seq) == + seq_or_req_seq) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int device_get_child_by_seq(struct udevice *parent, int seq, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = device_find_child_by_seq(parent, seq, false, &dev); + if (ret == -ENODEV) { + /* + * We didn't find it in probed devices. See if there is one + * that will request this seq if probed. + */ + ret = device_find_child_by_seq(parent, seq, true, &dev); + } + return device_get_device_tail(dev, ret, devp); +} + +int device_find_child_by_of_offset(struct udevice *parent, int of_offset, + struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (dev->of_offset == of_offset) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int device_get_child_by_of_offset(struct udevice *parent, int seq, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = device_find_child_by_of_offset(parent, seq, &dev); + return device_get_device_tail(dev, ret, devp); +} -- cgit v1.1 From e59f458de6999b8a786da857e653db6777f675ca Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:20 -0600 Subject: dm: Introduce per-child data for devices Some device types can have child devices and want to store information about them. For example a USB flash stick attached to a USB host controller would likely use this space. The controller can hold information about the USB state of each of its children. The data is stored attached to the child device in the 'parent_priv' member. It can be auto-allocated by dm when the child is probed. To do this, add a per_child_auto_alloc_size value to the parent driver. Signed-off-by: Simon Glass --- drivers/core/device.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 74bb5f0..42d250f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -218,6 +218,13 @@ static void device_free(struct udevice *dev) free(dev->uclass_priv); dev->uclass_priv = NULL; } + if (dev->parent) { + size = dev->parent->driver->per_child_auto_alloc_size; + if (size) { + free(dev->parent_priv); + dev->parent_priv = NULL; + } + } } int device_probe(struct udevice *dev) @@ -263,6 +270,15 @@ int device_probe(struct udevice *dev) /* Ensure all parents are probed */ if (dev->parent) { + size = dev->parent->driver->per_child_auto_alloc_size; + if (size) { + dev->parent_priv = calloc(1, size); + if (!dev->parent_priv) { + ret = -ENOMEM; + goto fail; + } + } + ret = device_probe(dev->parent); if (ret) goto fail; @@ -377,6 +393,16 @@ void *dev_get_priv(struct udevice *dev) return dev->priv; } +void *dev_get_parentdata(struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->parent_priv; +} + static int device_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) { -- cgit v1.1 From a327dee0f40bcdebaba1a3e47f2b9f1ceb970d2a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:21 -0600 Subject: dm: Add child_pre_probe() and child_post_remove() methods Some devices (particularly bus devices) must track their children, knowing when a new child is added so that it can be set up for communication on the bus. Add a child_pre_probe() method to provide this feature, and a corresponding child_post_remove() method. Signed-off-by: Simon Glass --- drivers/core/device.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index 42d250f..166b073 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -291,6 +291,12 @@ int device_probe(struct udevice *dev) } dev->seq = seq; + if (dev->parent && dev->parent->driver->child_pre_probe) { + ret = dev->parent->driver->child_pre_probe(dev); + if (ret) + goto fail; + } + if (drv->ofdata_to_platdata && dev->of_offset >= 0) { ret = drv->ofdata_to_platdata(dev); if (ret) @@ -352,12 +358,20 @@ int device_remove(struct udevice *dev) goto err_remove; } + if (dev->parent && dev->parent->driver->child_post_remove) { + ret = dev->parent->driver->child_post_remove(dev); + if (ret) { + dm_warn("%s: Device '%s' failed child_post_remove()", + __func__, dev->name); + } + } + device_free(dev); dev->seq = -1; dev->flags &= ~DM_FLAG_ACTIVATED; - return 0; + return ret; err_remove: /* We can't put the children back */ -- cgit v1.1 From 9b0ba067f96c2bbd4bcdf0128906877271eab548 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:22 -0600 Subject: dm: Improve errors and warnings in lists_bind_fdt() Add a debug message for when a device tree node has no driver. Also reword the warning when a device fails to bind, which was misleading. Signed-off-by: Simon Glass --- drivers/core/lists.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 5f1c85f..0f08bfd 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -124,16 +124,19 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset) const int n_ents = ll_entry_count(struct driver, driver); struct driver *entry; struct udevice *dev; + bool found = false; const char *name; int result = 0; - int ret; + int ret = 0; dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL)); for (entry = driver; entry != driver + n_ents; entry++) { ret = driver_check_compatible(blob, offset, entry->of_match); + name = fdt_get_name(blob, offset, NULL); if (ret == -ENOENT) { continue; } else if (ret == -ENODEV) { + dm_dbg("Device '%s' has no compatible string\n", name); break; } else if (ret) { dm_warn("Device tree error at offset %d\n", offset); @@ -142,14 +145,21 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset) break; } - name = fdt_get_name(blob, offset, NULL); dm_dbg(" - found match at '%s'\n", entry->name); ret = device_bind(parent, entry, name, NULL, offset, &dev); if (ret) { - dm_warn("No match for driver '%s'\n", entry->name); + dm_warn("Error binding driver '%s'\n", entry->name); if (!result || ret != -ENOENT) result = ret; + } else { + found = true; } + break; + } + + if (!found && !result && ret != -ENODEV) { + dm_dbg("No match for node '%s'\n", + fdt_get_name(blob, offset, NULL)); } return result; -- cgit v1.1 From bb58503d80808b973950ca425c7fb0bc6172a2bd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:23 -0600 Subject: dm: Add dm_scan_other() to locate board-specific devices Some boards will have devices which are not in the device tree and do not have platform data. They may be programnatically created, for example. Add a hook which boards can use to bind those devices early in boot. Signed-off-by: Simon Glass --- drivers/core/root.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index 4f9c7e7..393dd98 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -108,6 +108,11 @@ int dm_scan_fdt(const void *blob, bool pre_reloc_only) } #endif +__weak int dm_scan_other(bool pre_reloc_only) +{ + return 0; +} + int dm_init_and_scan(bool pre_reloc_only) { int ret; @@ -129,6 +134,9 @@ int dm_init_and_scan(bool pre_reloc_only) return ret; } #endif + ret = dm_scan_other(pre_reloc_only); + if (ret) + return ret; return 0; } -- cgit v1.1 From 74f96dada18edd3ebd4a1c5d39edc806a17eefcc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Jul 2014 06:55:24 -0600 Subject: dm: Give the demo uclass a name Uclasses should be named, so add a name for the demo uclass. Signed-off-by: Simon Glass --- drivers/demo/demo-uclass.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c index 636fd88..f6510d6 100644 --- a/drivers/demo/demo-uclass.c +++ b/drivers/demo/demo-uclass.c @@ -19,6 +19,7 @@ DECLARE_GLOBAL_DATA_PTR; UCLASS_DRIVER(demo) = { + .name = "demo", .id = UCLASS_DEMO, }; -- cgit v1.1 From 172437472af18ec6b40cd2fec558302c7cf117ce Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Tue, 24 Jun 2014 17:01:08 +0900 Subject: net: sh-eth: Add support R8A7794 R8A7794 has the same sh-ether IP core as other SH/rmobile. This patch adds support of R8A7794. Signed-off-by: Nobuhiro Iwamatsu --- drivers/net/sh_eth.c | 5 +++-- drivers/net/sh_eth.h | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 81e8ddb..451c33e 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -413,7 +413,8 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) #if defined(CONFIG_CPU_SH7734) || defined(CONFIG_R8A7740) sh_eth_write(eth, CONFIG_SH_ETHER_SH7734_MII, RMII_MII); -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7794) sh_eth_write(eth, sh_eth_read(eth, RMIIMR) | 0x1, RMIIMR); #endif /* Configure phy */ @@ -439,7 +440,7 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) #elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7752) sh_eth_write(eth, 1, RTRATE); #elif defined(CONFIG_CPU_SH7724) || defined(CONFIG_R8A7790) || \ - defined(CONFIG_R8A7791) + defined(CONFIG_R8A7791) || defined(CONFIG_R8A7794) val = ECMR_RTM; #endif } else if (phy->speed == 10) { diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h index d0d9aaa..e325a39 100644 --- a/drivers/net/sh_eth.h +++ b/drivers/net/sh_eth.h @@ -358,7 +358,8 @@ static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = { #elif defined(CONFIG_R8A7740) #define SH_ETH_TYPE_GETHER #define BASE_IO_ADDR 0xE9A00000 -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7794) #define SH_ETH_TYPE_ETHER #define BASE_IO_ADDR 0xEE700200 #elif defined(CONFIG_R7S72100) @@ -569,7 +570,8 @@ enum FELIC_MODE_BIT { ECMR_PRM = 0x00000001, #ifdef CONFIG_CPU_SH7724 ECMR_RTM = 0x00000010, -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7794) ECMR_RTM = 0x00000004, #endif -- cgit v1.1 From 2f972a3c62f775dd662f1710416a065f5f001dd5 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Tue, 24 Jun 2014 17:03:20 +0900 Subject: serial: sh: Add support R8A7794 This adds the preset value to register for R8A7794. Signed-off-by: Nobuhiro Iwamatsu --- drivers/serial/serial_sh.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index f5e9854..341997c 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -226,7 +226,8 @@ struct uart_port { # define SCSPTR3 0xffc60020 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* Overrun error bit */ # define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7794) # define SCIF_ORER 0x0001 # define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0, */ #else -- cgit v1.1 From 7a0227534dfc17c96bb02529fb69971d079a85f0 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 22 May 2014 14:37:10 +0530 Subject: drivers: net: cpsw: add support for using second port as ethernet Add support for using the second slave port of cpsw to be used as primary ethernet. Signed-off-by: Mugunthan V N --- drivers/net/cpsw.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index bd5fba2..8ec5161 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -211,6 +211,8 @@ struct cpdma_chan { #define chan_read(chan, fld) __raw_readl((chan)->fld) #define chan_read_ptr(chan, fld) ((void *)__raw_readl((chan)->fld)) +#define for_active_slave(slave, priv) \ + slave = (priv)->slaves + (priv)->data.active_slave; if (slave) #define for_each_slave(slave, priv) \ for (slave = (priv)->slaves; slave != (priv)->slaves + \ (priv)->data.slaves; slave++) @@ -609,7 +611,7 @@ static int cpsw_update_link(struct cpsw_priv *priv) int link = 0; struct cpsw_slave *slave; - for_each_slave(slave, priv) + for_active_slave(slave, priv) cpsw_slave_update_link(slave, priv, &link); priv->mdio_link = readl(&mdio_regs->link); return link; @@ -785,7 +787,7 @@ static int cpsw_init(struct eth_device *dev, bd_t *bis) ALE_SECURE); cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port); - for_each_slave(slave, priv) + for_active_slave(slave, priv) cpsw_slave_init(slave, priv); cpsw_update_link(priv); @@ -1013,7 +1015,7 @@ int cpsw_register(struct cpsw_platform_data *data) cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div); priv->bus = miiphy_get_dev_by_name(dev->name); - for_each_slave(slave, priv) + for_active_slave(slave, priv) cpsw_phy_init(dev, slave); return 1; -- cgit v1.1 From 7aa5598aac3faf9188559f7a50940df11c30b656 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 23 Jun 2014 16:06:29 -0400 Subject: tps65218/am43xx_evm: Add power framework support to TPS65218 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add in an init function for the drivers/power framework so we can dump and read the registers via i2c. Cc: Łukasz Majewski Signed-off-by: Tom Rini --- drivers/power/pmic/pmic_tps65218.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/power/pmic/pmic_tps65218.c b/drivers/power/pmic/pmic_tps65218.c index 0952456..dbc7a73 100644 --- a/drivers/power/pmic/pmic_tps65218.c +++ b/drivers/power/pmic/pmic_tps65218.c @@ -7,6 +7,8 @@ #include #include +#include +#include #include /** @@ -95,3 +97,23 @@ int tps65218_voltage_update(uchar dc_cntrl_reg, uchar volt_sel) return 0; } + +int power_tps65218_init(unsigned char bus) +{ + static const char name[] = "TPS65218_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = TPS65218_PMIC_NUM_OF_REGS; + p->hw.i2c.addr = TPS65218_CHIP_PM; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} -- cgit v1.1 From 67ac6ffaeefb93ff294f976cbb03479f84aa0b1a Mon Sep 17 00:00:00 2001 From: "Khoronzhuk, Ivan" Date: Fri, 4 Jul 2014 15:03:25 +0300 Subject: mtd: nand: davinci: add opportunity to write keystone U-boot image The Keystone SoCs use the same NAND driver as Davinci. This patch adds opportunity to write Keystone U-boot image to NAND device using appropriate RBL ECC layout. This is needed only if RBL boots U-boot from NAND device and that's supposed that raw u-boot partition is used only for writing image. The main problem is that default Davinci ECC layout is different from Keystone RBL layout. To read U-boot image the RBL needs that image was written using RBL ECC layout. The BBT table is written using default Davinci layout and has to be updated using one. The BBT can be updated only while erasing chip or by forced bad block assigning, so erase function has to use native ecc layout in order to be able to write BBT correctly. So if we're writing to NAND U-boot address we use RBL layout for others we use default ECC layout. Also remove definition for CONFIG_CMD_NAND_ECCLAYOUT as there is no reasons to use ECC layout commands. It was added by mistake. Signed-off-by: Ivan Khoronzhuk --- drivers/mtd/nand/davinci_nand.c | 196 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 5d42509..a079b1e 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -305,6 +305,189 @@ static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { #endif }; +#if defined CONFIG_KEYSTONE_RBL_NAND +#if defined(CONFIG_SYS_NAND_PAGE_2K) +static struct nand_ecclayout nand_keystone_rbl_4bit_layout_oobfirst = { + .eccbytes = 40, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + {.offset = 2, .length = 4, }, + {.offset = 16, .length = 6, }, + {.offset = 32, .length = 6, }, + {.offset = 48, .length = 6, }, + }, +#elif defined(CONFIG_SYS_NAND_PAGE_4K) + .eccbytes = 80, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 4, }, + {.offset = 16, .length = 6, }, + {.offset = 32, .length = 6, }, + {.offset = 48, .length = 6, }, + {.offset = 64, .length = 6, }, + {.offset = 80, .length = 6, }, + {.offset = 96, .length = 6, }, + {.offset = 112, .length = 6, }, + }, +#endif +}; + +#ifdef CONFIG_SYS_NAND_PAGE_2K +#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 11 +#elif defined(CONFIG_SYS_NAND_PAGE_4K) +#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 12 +#endif + +/** + * nand_davinci_write_page - write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page + */ +static int nand_davinci_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page, int cached, int raw) +{ + int status; + int ret = 0; + struct nand_ecclayout *saved_ecc_layout; + + /* save current ECC layout and assign Keystone RBL ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + saved_ecc_layout = chip->ecc.layout; + chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; + mtd->oobavail = chip->ecc.layout->oobavail; + } + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); + else + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) { + ret = status; + goto err; + } + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + /* + * See if operation failed and additional status checks are + * available. + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, page); + + if (status & NAND_STATUS_FAIL) { + ret = -EIO; + goto err; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) { + ret = -EIO; + goto err; + } + + /* Make sure the next page prog is preceded by a status read */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); +#endif +err: + /* restore ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = saved_ecc_layout; + mtd->oobavail = saved_ecc_layout->oobavail; + } + + return ret; +} + +/** + * nand_davinci_read_page_hwecc - hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers which need a special oob layout. + */ +static int nand_davinci_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint32_t *eccpos; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->buffers->ecccalc; + struct nand_ecclayout *saved_ecc_layout = chip->ecc.layout; + + /* save current ECC layout and assign Keystone RBL ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; + mtd->oobavail = chip->ecc.layout->oobavail; + } + + eccpos = chip->ecc.layout->eccpos; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + /* restore ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = saved_ecc_layout; + mtd->oobavail = saved_ecc_layout->oobavail; + } + + return 0; +} +#endif /* CONFIG_KEYSTONE_RBL_NAND */ + static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) { u32 val; @@ -604,6 +787,19 @@ static void nand_flash_init(void) void davinci_nand_init(struct nand_chip *nand) { +#if defined CONFIG_KEYSTONE_RBL_NAND + int i; + struct nand_ecclayout *layout; + + layout = &nand_keystone_rbl_4bit_layout_oobfirst; + layout->oobavail = 0; + for (i = 0; layout->oobfree[i].length && + i < ARRAY_SIZE(layout->oobfree); i++) + layout->oobavail += layout->oobfree[i].length; + + nand->write_page = nand_davinci_write_page; + nand->ecc.read_page = nand_davinci_read_page_hwecc; +#endif nand->chip_delay = 0; #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT nand->bbt_options |= NAND_BBT_USE_FLASH; -- cgit v1.1 From 7c387646060c6221836b332f5d0e0c9771ab99c1 Mon Sep 17 00:00:00 2001 From: "Khoronzhuk, Ivan" Date: Wed, 16 Jul 2014 00:59:25 +0300 Subject: keystone2: use CONFIG_SOC_KEYSTONE in common places Use CONFIG_SOC_KEYSTONE in common places instead of defining a lot of "if def .. || if def " for different Keystone2 SoC types. Acked-by: Murali Karicheri Signed-off-by: Ivan Khoronzhuk --- drivers/serial/ns16550.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index f26979d..8e7052d 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -30,7 +30,7 @@ #define serial_in(y) readb(y) #endif -#if defined(CONFIG_K2HK_EVM) +#if defined(CONFIG_SOC_KEYSTONE) #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 #define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) #undef UART_MCRVAL @@ -88,7 +88,7 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) /* /16 is proper to hit 115200 with 48MHz */ serial_out(0, &com_port->mdr1); #endif /* CONFIG_OMAP */ -#if defined(CONFIG_K2HK_EVM) +#if defined(CONFIG_SOC_KEYSTONE) serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); #endif } -- cgit v1.1