From 9ed42fa8668938f2362ef86ea5bccf67faaa1e47 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 18 Nov 2013 14:57:32 +0100 Subject: [PATCH 01/20] serial: ns16550: Add device ids for omap Signed-off-by: Sascha Hauer --- arch/arm/mach-omap/omap_devices.c | 4 +- drivers/serial/serial_ns16550.c | 62 +++++++++++++++++++++++++++---- drivers/serial/serial_ns16550.h | 4 -- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-omap/omap_devices.c b/arch/arm/mach-omap/omap_devices.c index 30cfdd06b..9ed3dcf61 100644 --- a/arch/arm/mach-omap/omap_devices.c +++ b/arch/arm/mach-omap/omap_devices.c @@ -21,6 +21,6 @@ static struct NS16550_plat serial_plat = { struct device_d *omap_add_uart(int id, unsigned long base) { - return add_ns16550_device(id, base, 1024, - IORESOURCE_MEM_8BIT, &serial_plat); + return add_generic_device("omap-uart", id, NULL, base, 1024, + IORESOURCE_MEM | IORESOURCE_MEM_8BIT, &serial_plat); } diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index d3a5a8176..a8487b0ed 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -55,6 +55,10 @@ static inline struct ns16550_priv *to_ns16550_priv(struct console_device *cdev) return container_of(cdev, struct ns16550_priv, cdev); } +struct ns16550_drvdata { + void (*init_port)(struct console_device *cdev); +}; + /** * @brief read register * @@ -178,14 +182,16 @@ static void ns16550_serial_init_port(struct console_device *cdev) /* initializing the device for the first time */ ns16550_write(cdev, 0x00, lcr); /* select ier reg */ ns16550_write(cdev, 0x00, ier); +} -#ifdef CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS - ns16550_write(cdev, 0x07, mdr1); /* Disable */ -#endif +#define omap_mdr1 8 -#ifdef CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS - ns16550_write(cdev, 0x00, mdr1); -#endif +static void ns16550_omap_init_port(struct console_device *cdev) +{ + ns16550_serial_init_port(cdev); + + ns16550_write(cdev, 0x07, omap_mdr1); /* Disable */ + ns16550_write(cdev, 0x00, omap_mdr1); } /*********** Exposed Functions **********************************/ @@ -239,6 +245,14 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) of_property_read_u32(np, "reg-shift", &priv->plat.shift); } +static struct ns16550_drvdata ns16550_drvdata = { + .init_port = ns16550_serial_init_port, +}; + +static __maybe_unused struct ns16550_drvdata omap_drvdata = { + .init_port = ns16550_omap_init_port, +}; + /** * @brief Probe entry point -called on the first match for device * @@ -253,8 +267,13 @@ static int ns16550_probe(struct device_d *dev) struct ns16550_priv *priv; struct console_device *cdev; struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; + struct ns16550_drvdata *devtype; int ret; + ret = dev_get_drvdata(dev, (unsigned long *)&devtype); + if (ret) + devtype = &ns16550_drvdata; + dev->priv = dev_request_mem_region(dev, 0); priv = xzalloc(sizeof(*priv)); @@ -294,7 +313,7 @@ static int ns16550_probe(struct device_d *dev) cdev->getc = ns16550_getc; cdev->setbrg = ns16550_setbaudrate; - ns16550_serial_init_port(cdev); + devtype->init_port(cdev); return console_register(cdev); @@ -307,8 +326,32 @@ err: static struct of_device_id ns16550_serial_dt_ids[] = { { .compatible = "ns16550a", - },{ + .data = (unsigned long)&ns16550_drvdata, + }, { .compatible = "snps,dw-apb-uart", + .data = (unsigned long)&ns16550_drvdata, + }, +#if IS_ENABLED(CONFIG_ARCH_OMAP) + { + .compatible = "ti,omap2-uart", + .data = (unsigned long)&omap_drvdata, + }, { + .compatible = "ti,omap3-uart", + .data = (unsigned long)&omap_drvdata, + }, { + .compatible = "ti,omap4-uart", + .data = (unsigned long)&omap_drvdata, + }, +#endif + { + /* sentinel */ + }, +}; + +static __maybe_unused struct platform_device_id ns16550_serial_ids[] = { + { + .name = "omap-uart", + .driver_data = (unsigned long)&omap_drvdata, }, { /* sentinel */ }, @@ -321,5 +364,8 @@ static struct driver_d ns16550_serial_driver = { .name = "ns16550_serial", .probe = ns16550_probe, .of_compatible = DRV_OF_COMPAT(ns16550_serial_dt_ids), +#if IS_ENABLED(CONFIG_ARCH_OMAP) + .id_table = ns16550_serial_ids, +#endif }; console_platform_driver(ns16550_serial_driver); diff --git a/drivers/serial/serial_ns16550.h b/drivers/serial/serial_ns16550.h index db8fe6499..c37d63ca9 100644 --- a/drivers/serial/serial_ns16550.h +++ b/drivers/serial/serial_ns16550.h @@ -40,10 +40,6 @@ #define lsr 5 #define msr 6 #define scr 7 -#ifdef CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS -#define mdr1 8 -#define osc_12m_sel 9 -#endif #define thr rbr #define iir fcr From 26c725874bc92ecf92885f86386da82d04a85b2c Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 19 Nov 2013 12:06:40 +0100 Subject: [PATCH 02/20] pinctrl: Add pinctrl-single driver Based on the kernel pinctrl-single driver. This one is just enough to make OMAP/AM33xx work. Signed-off-by: Sascha Hauer --- drivers/pinctrl/Kconfig | 3 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-single.c | 166 +++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-single.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 58397a0ac..825758692 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -23,6 +23,9 @@ config PINCTRL_IMX_IOMUX_V3 help This iomux controller is found on i.MX25,35,51,53,6. +config PINCTRL_SINGLE + bool "pinctrl single" + config PINCTRL_TEGRA20 select PINCTRL bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 169ed184c..b3b0fa9c5 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -2,4 +2,5 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o +obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c new file mode 100644 index 000000000..5c60c70b1 --- /dev/null +++ b/drivers/pinctrl/pinctrl-single.c @@ -0,0 +1,166 @@ +/* + * pinctrl-single - Generic device tree based pinctrl driver for one + * register per pin type pinmux controllers + * + * Copyright (c) 2013 Sascha Hauer + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +struct pinctrl_single { + void __iomem *base; + struct pinctrl_device pinctrl; + unsigned (*read)(void __iomem *reg); + void (*write)(unsigned val, void __iomem *reg); + unsigned width; +}; + +static unsigned __maybe_unused pcs_readb(void __iomem *reg) +{ + return readb(reg); +} + +static unsigned __maybe_unused pcs_readw(void __iomem *reg) +{ + return readw(reg); +} + +static unsigned __maybe_unused pcs_readl(void __iomem *reg) +{ + return readl(reg); +} + +static void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg) +{ + writeb(val, reg); +} + +static void __maybe_unused pcs_writew(unsigned val, void __iomem *reg) +{ + writew(val, reg); +} + +static void __maybe_unused pcs_writel(unsigned val, void __iomem *reg) +{ + writel(val, reg); +} + +static int pcs_set_state(struct pinctrl_device *pdev, struct device_node *np) +{ + struct pinctrl_single *pcs = container_of(pdev, struct pinctrl_single, pinctrl); + unsigned size = 0, index = 0; + const __be32 *mux; + + dev_dbg(pcs->pinctrl.dev, "set state: %s\n", np->full_name); + + mux = of_get_property(np, "pinctrl-single,pins", &size); + + size /= sizeof(*mux); /* Number of elements in array */ + + if (!mux || !size || (size & 1)) { + dev_err(pcs->pinctrl.dev, "bad data for mux %s\n", + np->full_name); + return -EINVAL; + } + + while (index < size) { + unsigned offset, val; + + offset = be32_to_cpup(mux + index++); + val = be32_to_cpup(mux + index++); + + pcs->write(val, pcs->base + offset); + } + + return 0; +} + +static struct pinctrl_ops pcs_ops = { + .set_state = pcs_set_state, +}; + +static int pcs_probe(struct device_d *dev) +{ + struct pinctrl_single *pcs; + struct device_node *np = dev->device_node; + int ret = 0; + + pcs = xzalloc(sizeof(*pcs)); + pcs->base = dev_request_mem_region(dev, 0); + pcs->pinctrl.dev = dev; + pcs->pinctrl.ops = &pcs_ops; + + ret = of_property_read_u32(np, "pinctrl-single,register-width", + &pcs->width); + if (ret) { + dev_dbg(dev, "no pinctrl-single,register-width property\n"); + goto out; + } + + switch (pcs->width) { + case 8: + pcs->read = pcs_readb; + pcs->write = pcs_writeb; + break; + case 16: + pcs->read = pcs_readw; + pcs->write = pcs_writew; + break; + case 32: + pcs->read = pcs_readl; + pcs->write = pcs_writel; + break; + default: + ret = -EINVAL; + dev_dbg(dev, "invalid register width: %d\n", pcs->width); + goto out; + } + + ret = pinctrl_register(&pcs->pinctrl); + if (ret) + goto out; + + return 0; + +out: + free(pcs); + + return ret; +} + +static __maybe_unused struct of_device_id pcs_dt_ids[] = { + { + .compatible = "pinctrl-single", + }, { + /* sentinel */ + } +}; + +static struct driver_d pcs_driver = { + .name = "pinctrl-single", + .probe = pcs_probe, + .of_compatible = DRV_OF_COMPAT(pcs_dt_ids), +}; + +static int pcs_init(void) +{ + return platform_driver_register(&pcs_driver); +} +postcore_initcall(pcs_init); From 186980340531bea3c98ff51c046e8a01d5892c84 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 19 Nov 2013 14:04:12 +0100 Subject: [PATCH 03/20] spi: omap: encode register offset into device_id The omap3 and omap4/am33xx spi cores differ in the offset of the registers in the address space. Instead of encoding this into the resources use the platform_device_id mechanism. This is done in preparation for devicetree probe support where the address space is in the devicetree and can't be adjusted. Signed-off-by: Sascha Hauer --- .../mach-omap/include/mach/am33xx-devices.h | 2 +- drivers/spi/omap3_spi.c | 38 ++++++++++++++++++- drivers/spi/omap3_spi.h | 6 ++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap/include/mach/am33xx-devices.h b/arch/arm/mach-omap/include/mach/am33xx-devices.h index 669198023..d2411a4fe 100644 --- a/arch/arm/mach-omap/include/mach/am33xx-devices.h +++ b/arch/arm/mach-omap/include/mach/am33xx-devices.h @@ -45,7 +45,7 @@ static inline struct device_d *am33xx_add_cpsw(struct cpsw_platform_data *cpsw_d static inline struct device_d *am33xx_add_spi(int id, resource_size_t start) { - return add_generic_device("omap3_spi", id, NULL, start + 0x100, SZ_4K - 0x100, + return add_generic_device("omap4-spi", id, NULL, start, SZ_4K, IORESOURCE_MEM, NULL); } diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 5c8cc8882..136fc18e8 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -49,6 +49,10 @@ #define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */ #define SPI_XFER_END 0x02 /* Deassert CS after transfer */ +struct omap_spi_drvdata { + unsigned register_offset; +}; + static void spi_reset(struct spi_master *master) { struct omap3_spi_master *omap3_master = container_of(master, struct omap3_spi_master, master); @@ -343,6 +347,12 @@ static int omap3_spi_probe(struct device_d *dev) { struct spi_master *master; struct omap3_spi_master *omap3_master; + struct omap_spi_drvdata *devtype; + int ret; + + ret = dev_get_drvdata(dev, (unsigned long *)&devtype); + if (ret) + return ret; omap3_master = xzalloc(sizeof(*omap3_master)); @@ -374,7 +384,10 @@ static int omap3_spi_probe(struct device_d *dev) master->setup = omap3_spi_setup; master->transfer = omap3_spi_transfer; - omap3_master->regs = dev_request_mem_region(dev, 0); + omap3_master->base = dev_request_mem_region(dev, 0); + omap3_master->regs = omap3_master->base; + + omap3_master->regs += devtype->register_offset; spi_reset(master); @@ -383,8 +396,29 @@ static int omap3_spi_probe(struct device_d *dev) return 0; } +static struct omap_spi_drvdata omap3_data = { + .register_offset = 0x0, +}; + +static struct omap_spi_drvdata omap4_data = { + .register_offset = 0x100, +}; + +static struct platform_device_id omap_spi_ids[] = { + { + .name = "omap3-spi", + .driver_data = (unsigned long)&omap3_data, + }, { + .name = "omap4-spi", + .driver_data = (unsigned long)&omap4_data, + }, { + /* sentinel */ + }, +}; + static struct driver_d omap3_spi_driver = { - .name = "omap3_spi", + .name = "omap-spi", .probe = omap3_spi_probe, + .id_table = omap_spi_ids, }; device_platform_driver(omap3_spi_driver); diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h index 55fd2fcc4..ce4a29bed 100644 --- a/drivers/spi/omap3_spi.h +++ b/drivers/spi/omap3_spi.h @@ -88,7 +88,11 @@ struct omap3_spi_master { struct spi_master master; - void __iomem *regs; + void __iomem *base; /* base of address space */ + void __iomem *regs; /* actual start of registers, omap4/am33xx have an + * offset of 0x100 between start of register space + * and registers + */ }; #endif /* _OMAP3_SPI_H_ */ From 5525385c78fb8efbeeb9fc98a38680c84f4ba8a3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 19 Nov 2013 14:06:35 +0100 Subject: [PATCH 04/20] spi: omap: Add devicetree probe support Signed-off-by: Sascha Hauer --- drivers/spi/omap3_spi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 136fc18e8..073a98f0c 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -404,6 +404,18 @@ static struct omap_spi_drvdata omap4_data = { .register_offset = 0x100, }; +static __maybe_unused struct of_device_id omap_spi_dt_ids[] = { + { + .compatible = "ti,omap2-mcspi", + .data = (unsigned long)&omap3_data, + }, { + .compatible = "ti,omap4-mcspi", + .data = (unsigned long)&omap4_data, + }, { + /* sentinel */ + } +}; + static struct platform_device_id omap_spi_ids[] = { { .name = "omap3-spi", @@ -419,6 +431,7 @@ static struct platform_device_id omap_spi_ids[] = { static struct driver_d omap3_spi_driver = { .name = "omap-spi", .probe = omap3_spi_probe, + .of_compatible = DRV_OF_COMPAT(omap_spi_dt_ids), .id_table = omap_spi_ids, }; device_platform_driver(omap3_spi_driver); From 7f5b61ab145725ef970c4b8b2d3ca47bb1fdc2f5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 19 Nov 2013 15:45:38 +0100 Subject: [PATCH 05/20] i2c: omap: Add devicetree support Signed-off-by: Sascha Hauer --- drivers/i2c/busses/i2c-omap.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index c45af92ab..f7108c260 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -151,6 +151,7 @@ struct omap_i2c_struct { u8 reg_shift; struct omap_i2c_driver_data *data; struct resource *ioarea; + u32 fclk_rate; u32 speed; /* Speed of bus in Khz */ u16 scheme; u16 cmd_err; @@ -271,6 +272,11 @@ static struct omap_i2c_driver_data am33xx_data = { .fclk_rate = 48000, }; +static struct omap_i2c_driver_data omap4_of_data = { + .flags = OMAP_I2C_FLAG_BUS_SHIFT_NONE, + .fclk_rate = 0, +}; + static inline void omap_i2c_write_reg(struct omap_i2c_struct *i2c_omap, int reg, u16 val) { @@ -415,7 +421,7 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap) internal_clk = 4000; /* Compute prescaler divisor */ - psc = i2c_data->fclk_rate / internal_clk; + psc = i2c_omap->fclk_rate / internal_clk; psc = psc - 1; /* If configured for High Speed */ @@ -1074,6 +1080,12 @@ i2c_omap_probe(struct device_d *pdev) i2c_omap->b_hw = 1; /* Enable hardware fixes */ } + i2c_omap->fclk_rate = i2c_data->fclk_rate; + + if (!i2c_omap->fclk_rate) + of_property_read_u32(pdev->device_node, "clock-frequency", + &i2c_omap->fclk_rate); + /* reset ASAP, clearing any IRQs */ omap_i2c_init(i2c_omap); @@ -1085,6 +1097,7 @@ i2c_omap_probe(struct device_d *pdev) i2c_omap->adapter.master_xfer = omap_i2c_xfer, i2c_omap->adapter.nr = pdev->id; i2c_omap->adapter.dev.parent = pdev; + i2c_omap->adapter.dev.device_node = pdev->device_node; /* i2c device drivers may be active on return from add_adapter() */ r = i2c_add_numbered_adapter(&i2c_omap->adapter); @@ -1119,10 +1132,23 @@ static struct platform_device_id omap_i2c_ids[] = { }, }; +static __maybe_unused struct of_device_id omap_spi_dt_ids[] = { + { + .compatible = "ti,omap3-i2c", + .data = (unsigned long)&omap3_data, + }, { + .compatible = "ti,omap4-i2c", + .data = (unsigned long)&omap4_of_data, + }, { + /* sentinel */ + } +}; + static struct driver_d omap_i2c_driver = { .probe = i2c_omap_probe, .name = DRIVER_NAME, .id_table = omap_i2c_ids, + .of_compatible = DRV_OF_COMPAT(omap_spi_dt_ids), }; device_platform_driver(omap_i2c_driver); From e586a64d3e3c2e5fb6363271fb079126b7e9fa0f Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 08:56:05 +0100 Subject: [PATCH 06/20] net: cpsw: inline slave_data Devicetree probed cpsw devices won't have platform_data, so inline the fields from slave_data instead of keeping a pointer. Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index e51a8b1b6..25ba3d905 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -175,7 +175,8 @@ struct cpsw_slave { struct cpsw_sliver_regs *sliver; int slave_num; u32 mac_control; - struct cpsw_slave_data *data; + int phy_id; + phy_interface_t phy_if; }; struct cpdma_desc { @@ -765,13 +766,13 @@ static int cpsw_init(struct eth_device *edev) static int cpsw_open(struct eth_device *edev) { struct cpsw_priv *priv = edev->priv; - struct cpsw_slave_data *slave_data = priv->data.slave_data; + struct cpsw_slave *slave = &priv->slaves[0]; int i, ret; dev_dbg(priv->dev, "* %s\n", __func__); - ret = phy_device_connect(edev, &priv->miibus, slave_data[0].phy_id, - cpsw_adjust_link, 0, slave_data[0].phy_if); + ret = phy_device_connect(edev, &priv->miibus, slave->phy_id, + cpsw_adjust_link, 0, slave->phy_if); if (ret) return ret; @@ -908,7 +909,8 @@ static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, dev_dbg(priv->dev, "* %s\n", __func__); slave->slave_num = slave_num; - slave->data = data; + slave->phy_id = data->phy_id; + slave->phy_if = data->phy_if; slave->regs = regs + priv->slave_ofs + priv->slave_size * slave_num; slave->sliver = regs + priv->sliver_ofs + SLIVER_SIZE * slave_num; } From 6d2f0d57beeda5f380a20f5b31b6f0cd431fe377 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 27 Nov 2013 10:31:27 +0100 Subject: [PATCH 07/20] pinctrl: Add functions to select pinctrl from device_node Instead of requiring a device pointer, add a functions to select the pinctrl state based on a device node. The AM33xx cpsw devicetree description has several subdevices with pinctrl information attached to them. In barebox we do not handle the subdevices as distinct devices, so the pinctrl is never configured correctly and the mdio bus subdevice stops working. This patch makes it possible to configure the pinctrl without having a device. Signed-off-by: Sascha Hauer --- drivers/pinctrl/pinctrl.c | 24 ++++++++++++++++++------ include/pinctrl.h | 12 ++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c index 7c797d35b..de8361d15 100644 --- a/drivers/pinctrl/pinctrl.c +++ b/drivers/pinctrl/pinctrl.c @@ -49,7 +49,7 @@ static int pinctrl_config_one(struct device_node *np) return pdev->ops->set_state(pdev, np); } -int pinctrl_select_state(struct device_d *dev, const char *name) +int of_pinctrl_select_state(struct device_node *np, const char *name) { int state, ret; char *propname; @@ -57,13 +57,9 @@ int pinctrl_select_state(struct device_d *dev, const char *name) const __be32 *list; int size, config; phandle phandle; - struct device_node *np_config, *np; + struct device_node *np_config; const char *statename; - np = dev->device_node; - if (!np) - return 0; - if (!of_find_property(np, "pinctrl-0", NULL)) return 0; @@ -125,6 +121,22 @@ err: return ret; } +int of_pinctrl_select_state_default(struct device_node *np) +{ + return of_pinctrl_select_state(np, "default"); +} + +int pinctrl_select_state(struct device_d *dev, const char *name) +{ + struct device_node *np; + + np = dev->device_node; + if (!np) + return 0; + + return of_pinctrl_select_state(np, name); +} + int pinctrl_select_state_default(struct device_d *dev) { return pinctrl_select_state(dev, "default"); diff --git a/include/pinctrl.h b/include/pinctrl.h index 7323f8b2f..0f03b10be 100644 --- a/include/pinctrl.h +++ b/include/pinctrl.h @@ -20,6 +20,8 @@ void pinctrl_unregister(struct pinctrl_device *pdev); #ifdef CONFIG_PINCTRL int pinctrl_select_state(struct device_d *dev, const char *state); int pinctrl_select_state_default(struct device_d *dev); +int of_pinctrl_select_state(struct device_node *np, const char *state); +int of_pinctrl_select_state_default(struct device_node *np); #else static inline int pinctrl_select_state(struct device_d *dev, const char *state) { @@ -30,6 +32,16 @@ static inline int pinctrl_select_state_default(struct device_d *dev) { return -ENODEV; } + +static inline int of_pinctrl_select_state(struct device_node *np, const char *state) +{ + return -ENODEV; +} + +static inline int of_pinctrl_select_state_default(struct device_node *np) +{ + return -ENODEV; +} #endif #endif /* PINCTRL_H */ From 9321538ee26ee53ebdba5f8efeb069abe653589b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 09:19:36 +0100 Subject: [PATCH 08/20] cpsw: Add devicetree probe support Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 166 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 7 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 25ba3d905..ec4583487 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -900,17 +904,23 @@ static int cpsw_recv(struct eth_device *dev) return 0; } +static void cpsw_slave_init_data(struct cpsw_slave *slave, int slave_num, + struct cpsw_priv *priv) +{ + struct cpsw_slave_data *data = priv->data.slave_data + slave_num; + + slave->phy_id = data->phy_id; + slave->phy_if = data->phy_if; +} + static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, struct cpsw_priv *priv) { void *regs = priv->regs; - struct cpsw_slave_data *data = priv->data.slave_data + slave_num; dev_dbg(priv->dev, "* %s\n", __func__); slave->slave_num = slave_num; - slave->phy_id = data->phy_id; - slave->phy_if = data->phy_if; slave->regs = regs + priv->slave_ofs + priv->slave_size * slave_num; slave->sliver = regs + priv->sliver_ofs + SLIVER_SIZE * slave_num; } @@ -950,6 +960,130 @@ static struct cpsw_data cpsw2_data = { .cppi_ram_ofs = 0x2000, }; +static void __iomem *phy_sel_addr; +static bool rmii_clock_external; + +static int cpsw_phy_sel_init(struct cpsw_priv *priv, struct device_node *np) +{ + const void *reg; + unsigned long addr; + + reg = of_get_property(np, "reg", NULL); + if (!reg) + return -EINVAL; + + addr = of_translate_address(np, reg); + + phy_sel_addr = (void *)addr; + + if (of_property_read_bool(np, "rmii-clock-ext")) + rmii_clock_external = true; + + return 0; +} + +/* AM33xx SoC specific definitions for the CONTROL port */ +#define AM33XX_GMII_SEL_MODE_MII 0 +#define AM33XX_GMII_SEL_MODE_RMII 1 +#define AM33XX_GMII_SEL_MODE_RGMII 2 + +#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7) +#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6) + +static void cpsw_gmii_sel_am335x(struct cpsw_slave *slave) +{ + u32 reg; + u32 mask; + u32 mode = 0; + + reg = readl(phy_sel_addr); + + switch (slave->phy_if) { + case PHY_INTERFACE_MODE_RMII: + mode = AM33XX_GMII_SEL_MODE_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + mode = AM33XX_GMII_SEL_MODE_RGMII; + break; + + case PHY_INTERFACE_MODE_MII: + default: + mode = AM33XX_GMII_SEL_MODE_MII; + break; + }; + + mask = 0x3 << (slave->slave_num * 2) | BIT(slave->slave_num + 6); + mode <<= slave->slave_num * 2; + + if (rmii_clock_external) { + if (slave->slave_num == 0) + mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; + else + mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN; + } + + reg &= ~mask; + reg |= mode; + + writel(reg, phy_sel_addr); +} + +static int cpsw_probe_dt(struct cpsw_priv *priv) +{ + struct device_d *dev = priv->dev; + struct device_node *np = dev->device_node, *child; + int ret, i = 0; + + ret = of_property_read_u32(np, "slaves", &priv->num_slaves); + if (ret) + return ret; + + priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, "ti,am3352-cpsw-phy-sel")) { + ret = cpsw_phy_sel_init(priv, child); + if (ret) + return ret; + } + + if (of_device_is_compatible(child, "ti,davinci_mdio")) { + ret = of_pinctrl_select_state_default(child); + if (ret) + return ret; + } + + if (!strncmp(child->name, "slave", 5)) { + struct cpsw_slave *slave = &priv->slaves[i]; + uint32_t phy_id[2]; + + ret = of_property_read_u32_array(child, "phy_id", phy_id, 2); + if (ret) + return ret; + + slave->phy_id = phy_id[1]; + slave->phy_if = of_get_phy_mode(child); + + i++; + } + } + + for (i = 0; i < 2; i++) { + struct cpsw_slave *slave = &priv->slaves[i]; + + cpsw_gmii_sel_am335x(slave); + } + + /* Only one slave supported by this driver */ + priv->num_slaves = 1; + + return 0; +} + int cpsw_probe(struct device_d *dev) { struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data; @@ -959,6 +1093,7 @@ int cpsw_probe(struct device_d *dev) uint64_t start; uint32_t phy_mask; struct cpsw_data *cpsw_data; + int ret; dev_dbg(dev, "* %s\n", __func__); @@ -966,14 +1101,22 @@ int cpsw_probe(struct device_d *dev) priv = xzalloc(sizeof(*priv)); priv->dev = dev; - priv->data = *data; + + if (dev->device_node) { + ret = cpsw_probe_dt(priv); + if (ret) + return ret; + } else { + priv->data = *data; + priv->num_slaves = data->num_slaves; + priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); + for_each_slave(priv, cpsw_slave_init_data, idx, priv); + } + priv->channels = 8; - priv->num_slaves = data->num_slaves; priv->ale_entries = 1024; edev = &priv->edev; - priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); - priv->host_port = 0; priv->regs = regs; @@ -1059,8 +1202,17 @@ int cpsw_probe(struct device_d *dev) return 0; } +static __maybe_unused struct of_device_id cpsw_dt_ids[] = { + { + .compatible = "ti,cpsw", + }, { + /* sentinel */ + } +}; + static struct driver_d cpsw_driver = { .name = "cpsw", .probe = cpsw_probe, + .of_compatible = DRV_OF_COMPAT(cpsw_dt_ids), }; device_platform_driver(cpsw_driver); From a722418645c94098d0080a307685cfc363a23a94 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 09:32:35 +0100 Subject: [PATCH 09/20] net: cpsw: move eth_device into slave An ethernet device is per slave, not per cpsw. Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index ec4583487..adffb53ec 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -181,6 +181,7 @@ struct cpsw_slave { u32 mac_control; int phy_id; phy_interface_t phy_if; + struct eth_device edev; }; struct cpdma_desc { @@ -201,7 +202,6 @@ struct cpdma_chan { struct cpsw_priv { struct device_d *dev; - struct eth_device edev; struct mii_bus miibus; u32 version; @@ -577,7 +577,7 @@ static int cpsw_set_hwaddr(struct eth_device *edev, unsigned char *mac) static void cpsw_slave_update_link(struct cpsw_slave *slave, struct cpsw_priv *priv, int *link) { - struct phy_device *phydev = priv->edev.phydev; + struct phy_device *phydev = slave->edev.phydev; u32 mac_control = 0; dev_dbg(priv->dev, "* %s\n", __func__); @@ -917,12 +917,25 @@ static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, struct cpsw_priv *priv) { void *regs = priv->regs; + struct eth_device *edev = &slave->edev; dev_dbg(priv->dev, "* %s\n", __func__); slave->slave_num = slave_num; slave->regs = regs + priv->slave_ofs + priv->slave_size * slave_num; slave->sliver = regs + priv->sliver_ofs + SLIVER_SIZE * slave_num; + + edev->priv = priv; + edev->init = cpsw_init; + edev->open = cpsw_open; + edev->halt = cpsw_halt; + edev->send = cpsw_send; + edev->recv = cpsw_recv; + edev->get_ethaddr = cpsw_get_hwaddr; + edev->set_ethaddr = cpsw_set_hwaddr; + edev->parent = priv->dev; + + eth_register(edev); } struct cpsw_data { @@ -1089,7 +1102,6 @@ int cpsw_probe(struct device_d *dev) struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data; struct cpsw_priv *priv; void __iomem *regs; - struct eth_device *edev; uint64_t start; uint32_t phy_mask; struct cpsw_data *cpsw_data; @@ -1115,7 +1127,6 @@ int cpsw_probe(struct device_d *dev) priv->channels = 8; priv->ale_entries = 1024; - edev = &priv->edev; priv->host_port = 0; priv->regs = regs; @@ -1144,18 +1155,6 @@ int cpsw_probe(struct device_d *dev) priv->slave_size = cpsw_data->slave_size; priv->sliver_ofs = cpsw_data->sliver_ofs; - for_each_slave(priv, cpsw_slave_setup, idx, priv); - - edev->priv = priv; - edev->init = cpsw_init; - edev->open = cpsw_open; - edev->halt = cpsw_halt; - edev->send = cpsw_send; - edev->recv = cpsw_recv; - edev->get_ethaddr = cpsw_get_hwaddr; - edev->set_ethaddr = cpsw_set_hwaddr; - edev->parent = dev; - priv->miibus.read = cpsw_mdio_read; priv->miibus.write = cpsw_mdio_write; priv->miibus.priv = priv; @@ -1197,7 +1196,7 @@ int cpsw_probe(struct device_d *dev) mdiobus_register(&priv->miibus); - eth_register(edev); + for_each_slave(priv, cpsw_slave_setup, idx, priv); return 0; } From 82bc71b820b91c6a46a052fb13e2d68efa84dee6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 10:09:01 +0100 Subject: [PATCH 10/20] net: cpsw: drop for_each_slave We currently only use one slave, so drop for_each_slave and hardcode slave[0] until we pass the proper context to the functions that makes this hack unnecessary. Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index adffb53ec..b041b531f 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -228,12 +228,6 @@ struct cpsw_priv { struct cpdma_chan rx_chan, tx_chan; struct cpsw_slave *slaves; -#define for_each_slave(priv, func, arg...) \ - do { \ - int idx; \ - for (idx = 0; idx < (priv)->num_slaves; idx++) \ - (func)((priv)->slaves + idx, ##arg); \ - } while (0) }; static int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) @@ -542,16 +536,6 @@ static inline void soft_reset(struct cpsw_priv *priv, void *reg) ((mac)[2] << 16) | ((mac)[3] << 24)) #define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) -static void cpsw_set_slave_mac(struct cpsw_slave *slave, - struct cpsw_priv *priv, - unsigned char *mac) -{ - dev_dbg(priv->dev, "* %s\n", __func__); - - writel(mac_hi(mac), &slave->regs->sa_hi); - writel(mac_lo(mac), &slave->regs->sa_lo); -} - static int cpsw_get_hwaddr(struct eth_device *edev, unsigned char *mac) { struct cpsw_priv *priv = edev->priv; @@ -564,12 +548,14 @@ static int cpsw_get_hwaddr(struct eth_device *edev, unsigned char *mac) static int cpsw_set_hwaddr(struct eth_device *edev, unsigned char *mac) { struct cpsw_priv *priv = edev->priv; + struct cpsw_slave *slave = &priv->slaves[0]; dev_dbg(priv->dev, "* %s\n", __func__); memcpy(&priv->mac_addr, mac, sizeof(priv->mac_addr)); - for_each_slave(priv, cpsw_set_slave_mac, priv, mac); + writel(mac_hi(mac), &slave->regs->sa_hi); + writel(mac_lo(mac), &slave->regs->sa_lo); return 0; } @@ -617,22 +603,25 @@ static void cpsw_slave_update_link(struct cpsw_slave *slave, slave->mac_control = mac_control; } -static int cpsw_update_link(struct cpsw_priv *priv) +static int cpsw_update_link(struct cpsw_slave *slave, struct cpsw_priv *priv) { int link = 0; dev_dbg(priv->dev, "* %s\n", __func__); - for_each_slave(priv, cpsw_slave_update_link, priv, &link); + cpsw_slave_update_link(slave, priv, &link); + return link; } -static void cpsw_adjust_link(struct eth_device *edev) { +static void cpsw_adjust_link(struct eth_device *edev) +{ struct cpsw_priv *priv = edev->priv; + struct cpsw_slave *slave = &priv->slaves[0]; dev_dbg(priv->dev, "* %s\n", __func__); - cpsw_update_link(priv); + cpsw_update_link(slave, priv); } static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) @@ -805,9 +794,8 @@ static int cpsw_open(struct eth_device *edev) ALE_SECURE); cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port); - for_each_slave(priv, cpsw_slave_init, priv); - - cpsw_update_link(priv); + cpsw_slave_init(&priv->slaves[0], priv); + cpsw_update_link(&priv->slaves[0], priv); /* init descriptor pool */ for (i = 0; i < NUM_DESCS; i++) { @@ -1122,7 +1110,7 @@ int cpsw_probe(struct device_d *dev) priv->data = *data; priv->num_slaves = data->num_slaves; priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); - for_each_slave(priv, cpsw_slave_init_data, idx, priv); + cpsw_slave_init_data(&priv->slaves[0], 0, priv); } priv->channels = 8; @@ -1196,7 +1184,7 @@ int cpsw_probe(struct device_d *dev) mdiobus_register(&priv->miibus); - for_each_slave(priv, cpsw_slave_setup, idx, priv); + cpsw_slave_setup(&priv->slaves[0], 0, priv); return 0; } From 046157602e551f4bbe5071916b3360361a99f2a5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 10:18:53 +0100 Subject: [PATCH 11/20] net: cpsw: attach slave to edev->priv An ethernet device belongs to a slave, so set edev->priv to the slave and not to the cpsw. Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index b041b531f..6141328af 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -182,6 +182,7 @@ struct cpsw_slave { int phy_id; phy_interface_t phy_if; struct eth_device edev; + struct cpsw_priv *cpsw; }; struct cpdma_desc { @@ -538,7 +539,8 @@ static inline void soft_reset(struct cpsw_priv *priv, void *reg) static int cpsw_get_hwaddr(struct eth_device *edev, unsigned char *mac) { - struct cpsw_priv *priv = edev->priv; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; dev_dbg(priv->dev, "* %s\n", __func__); @@ -547,8 +549,8 @@ static int cpsw_get_hwaddr(struct eth_device *edev, unsigned char *mac) static int cpsw_set_hwaddr(struct eth_device *edev, unsigned char *mac) { - struct cpsw_priv *priv = edev->priv; - struct cpsw_slave *slave = &priv->slaves[0]; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; dev_dbg(priv->dev, "* %s\n", __func__); @@ -616,8 +618,8 @@ static int cpsw_update_link(struct cpsw_slave *slave, struct cpsw_priv *priv) static void cpsw_adjust_link(struct eth_device *edev) { - struct cpsw_priv *priv = edev->priv; - struct cpsw_slave *slave = &priv->slaves[0]; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; dev_dbg(priv->dev, "* %s\n", __func__); @@ -758,8 +760,8 @@ static int cpsw_init(struct eth_device *edev) static int cpsw_open(struct eth_device *edev) { - struct cpsw_priv *priv = edev->priv; - struct cpsw_slave *slave = &priv->slaves[0]; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; int i, ret; dev_dbg(priv->dev, "* %s\n", __func__); @@ -843,9 +845,10 @@ static int cpsw_open(struct eth_device *edev) return 0; } -static void cpsw_halt(struct eth_device *dev) +static void cpsw_halt(struct eth_device *edev) { - struct cpsw_priv *priv = dev->priv; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; writel(0, priv->dma_regs + CPDMA_TXCONTROL); writel(0, priv->dma_regs + CPDMA_RXCONTROL); @@ -857,9 +860,10 @@ static void cpsw_halt(struct eth_device *dev) soft_reset(priv, priv->dma_regs + CPDMA_SOFTRESET); } -static int cpsw_send(struct eth_device *dev, void *packet, int length) +static int cpsw_send(struct eth_device *edev, void *packet, int length) { - struct cpsw_priv *priv = dev->priv; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; void *buffer; int ret, len; @@ -877,9 +881,10 @@ static int cpsw_send(struct eth_device *dev, void *packet, int length) return ret; } -static int cpsw_recv(struct eth_device *dev) +static int cpsw_recv(struct eth_device *edev) { - struct cpsw_priv *priv = dev->priv; + struct cpsw_slave *slave = edev->priv; + struct cpsw_priv *priv = slave->cpsw; void *buffer; int len; @@ -912,8 +917,9 @@ static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, slave->slave_num = slave_num; slave->regs = regs + priv->slave_ofs + priv->slave_size * slave_num; slave->sliver = regs + priv->sliver_ofs + SLIVER_SIZE * slave_num; + slave->cpsw = priv; - edev->priv = priv; + edev->priv = slave; edev->init = cpsw_init; edev->open = cpsw_open; edev->halt = cpsw_halt; From 7cc4a2ad95d5037d5362696cd71209c43ccb803b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 10:21:24 +0100 Subject: [PATCH 12/20] net: cpsw: straighten error path This mainly has the effect of checking the return value of eth_register. Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 6141328af..5e68e1be6 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -906,7 +906,7 @@ static void cpsw_slave_init_data(struct cpsw_slave *slave, int slave_num, slave->phy_if = data->phy_if; } -static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, +static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, struct cpsw_priv *priv) { void *regs = priv->regs; @@ -929,7 +929,7 @@ static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, edev->set_ethaddr = cpsw_set_hwaddr; edev->parent = priv->dev; - eth_register(edev); + return eth_register(edev); } struct cpsw_data { @@ -1111,7 +1111,7 @@ int cpsw_probe(struct device_d *dev) if (dev->device_node) { ret = cpsw_probe_dt(priv); if (ret) - return ret; + goto out; } else { priv->data = *data; priv->num_slaves = data->num_slaves; @@ -1135,7 +1135,8 @@ int cpsw_probe(struct device_d *dev) cpsw_data = &cpsw2_data; break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } priv->descs = regs + cpsw_data->cppi_ram_ofs; @@ -1190,9 +1191,16 @@ int cpsw_probe(struct device_d *dev) mdiobus_register(&priv->miibus); - cpsw_slave_setup(&priv->slaves[0], 0, priv); + ret = cpsw_slave_setup(&priv->slaves[0], 0, priv); + if (ret) + goto out; return 0; +out: + free(priv->slaves); + free(priv); + + return ret; } static __maybe_unused struct of_device_id cpsw_dt_ids[] = { From 7603f7442bdd473c6986279c41ee7a7321ba1847 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 21 Nov 2013 08:21:21 +0100 Subject: [PATCH 13/20] gpio: omap: move to drivers/gpio/ Signed-off-by: Sascha Hauer --- arch/arm/mach-omap/Makefile | 1 - drivers/gpio/Kconfig | 3 +++ drivers/gpio/Makefile | 1 + arch/arm/mach-omap/gpio.c => drivers/gpio/gpio-omap.c | 0 4 files changed, 4 insertions(+), 1 deletion(-) rename arch/arm/mach-omap/gpio.c => drivers/gpio/gpio-omap.c (100%) diff --git a/arch/arm/mach-omap/Makefile b/arch/arm/mach-omap/Makefile index cf2d5ee7d..06999b3f6 100644 --- a/arch/arm/mach-omap/Makefile +++ b/arch/arm/mach-omap/Makefile @@ -31,5 +31,4 @@ obj-$(CONFIG_SHELL_NONE) += xload.o obj-$(CONFIG_MFD_TWL6030) += omap4_twl6030_mmc.o obj-$(CONFIG_OMAP4_USBBOOT) += omap4_rom_usb.o obj-$(CONFIG_CMD_BOOT_ORDER) += boot_order.o -obj-y += gpio.o obj-$(CONFIG_BAREBOX_UPDATE_AM33XX_SPI_NOR_MLO) += am33xx_bbu_spi_mlo.o diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 18d3135eb..7c426563e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,6 +30,9 @@ config GPIO_GENERIC_PLATFORM config GPIO_IMX def_bool ARCH_IMX +config GPIO_OMAP + def_bool ARCH_OMAP + config GPIO_ORION bool "GPIO support for Marvell Orion/MVEBU SoCs" depends on ARCH_MVEBU diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index dc9fb13d2..b7c536d21 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_IMX) += gpio-imx.o obj-$(CONFIG_GPIO_ORION) += gpio-orion.o +obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o diff --git a/arch/arm/mach-omap/gpio.c b/drivers/gpio/gpio-omap.c similarity index 100% rename from arch/arm/mach-omap/gpio.c rename to drivers/gpio/gpio-omap.c From 5c8ea4ff9a0f305334d3aa73a35a3898bd2a61f6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 21 Nov 2013 09:15:55 +0100 Subject: [PATCH 14/20] omap: gpmc: some refactoring - make debug messages more meaningful - calculate value once and use it to print and configure instead of calculating it twice Signed-off-by: Sascha Hauer --- arch/arm/mach-omap/gpmc.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-omap/gpmc.c b/arch/arm/mach-omap/gpmc.c index bb84b3865..dae7630b6 100644 --- a/arch/arm/mach-omap/gpmc.c +++ b/arch/arm/mach-omap/gpmc.c @@ -111,7 +111,10 @@ void gpmc_cs_config(char cs, struct gpmc_config *config) { void __iomem *reg = GPMC_REG(CONFIG1_0) + (cs * GPMC_CONFIG_CS_SIZE); unsigned char x = 0; - debug("gpmccs=0x%x cfg=0x%p\n", cs, config); + uint32_t config7; + + debug("%s: cs=%d base=0x%08x size=0x%08x\n", __func__, + cs, config->base, config->size); /* Disable the CS before reconfiguring */ writel(0x0, GPMC_REG(CONFIG7_0) + (cs * GPMC_CONFIG_CS_SIZE)); @@ -119,22 +122,22 @@ void gpmc_cs_config(char cs, struct gpmc_config *config) /* Write the CFG1-6 regs */ while (x < 6) { - debug("gpmccfg%d Reg:0x%p <-0x%08x\n", - x, reg, config->cfg[x]); + debug("gpmccfg%d Reg:0x%p <-0x%08x, old 0x%08x\n", + x, reg, config->cfg[x], readl(reg)); writel(config->cfg[x], reg); reg += GPMC_CONFIG_REG_OFF; x++; } - /* reg now points to CFG7 */ - debug("gpmccfg%d Reg:0x%p <-0x%08x\n", - x, reg, (0x1 << 6) | /* CS enable */ - ((config->size & 0xF) << 8) | /* Size */ - ((config->base >> 24) & 0x3F)); - writel((0x1 << 6) | /* CS enable */ + config7 = (0x1 << 6) | /* CS enable */ ((config->size & 0xF) << 8) | /* Size */ - ((config->base >> 24) & 0x3F), /* Address */ - reg); + ((config->base >> 24) & 0x3F); + + debug("gpmccfg%d Reg:0x%p <-0x%08x, old 0x%08x\n", + x, reg, config7, readl(reg)); + + writel(config7, reg); + mdelay(1); /* Settling time */ } EXPORT_SYMBOL(gpmc_cs_config); From 15d60e523086a642ced3bf4f07e2666098845fc6 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 21 Nov 2013 11:19:36 +0100 Subject: [PATCH 15/20] gpio: omap: Add devicetree probe support This adds devicetree probe support for the OMAP gpio ports and also makes sure the corresponding platform_devices don't get registered when they are already present from devicetree. Signed-off-by: Sascha Hauer --- drivers/gpio/gpio-omap.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 49ffbda50..e66a614f2 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -48,6 +48,18 @@ struct omap_gpio_chip { struct gpio_chip chip; }; +struct omap_gpio_drvdata { + unsigned int regofs; +}; + +static struct omap_gpio_drvdata gpio_omap3_drvdata = { + .regofs = 0x0, +}; + +static struct omap_gpio_drvdata gpio_omap4_drvdata = { + .regofs = 0x100, +}; + static inline int omap_get_gpio_index(int gpio) { return gpio & 0x1f; @@ -129,11 +141,24 @@ static struct gpio_ops omap_gpio_ops = { static int omap_gpio_probe(struct device_d *dev) { struct omap_gpio_chip *omapgpio; + struct omap_gpio_drvdata *drvdata = NULL; + + dev_get_drvdata(dev, (unsigned long *)&drvdata); omapgpio = xzalloc(sizeof(*omapgpio)); omapgpio->base = dev_request_mem_region(dev, 0); + if (drvdata) + omapgpio->base += drvdata->regofs; + omapgpio->chip.ops = &omap_gpio_ops; - omapgpio->chip.base = dev->id * 32; + if (dev->id < 0) { + omapgpio->chip.base = of_alias_get_id(dev->device_node, "gpio"); + if (omapgpio->chip.base < 0) + return omapgpio->chip.base; + omapgpio->chip.base *= 32; + } else { + omapgpio->chip.base = dev->id * 32; + } omapgpio->chip.ngpio = 32; omapgpio->chip.dev = dev; gpiochip_add(&omapgpio->chip); @@ -144,9 +169,21 @@ static int omap_gpio_probe(struct device_d *dev) return 0; } +static __maybe_unused struct of_device_id omap_gpio_dt_ids[] = { + { + .compatible = "ti,omap4-gpio", + .data = (unsigned long)&gpio_omap4_drvdata, + }, { + .compatible = "ti,omap3-gpio", + .data = (unsigned long)&gpio_omap3_drvdata, + }, { + } +}; + static struct driver_d omap_gpio_driver = { .name = "omap-gpio", .probe = omap_gpio_probe, + .of_compatible = DRV_OF_COMPAT(omap_gpio_dt_ids), }; static int omap_gpio_add(void) From b0cd10ff12a01ada7f2f0833db42a65cc32beab8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 21 Nov 2013 12:06:49 +0100 Subject: [PATCH 16/20] mtd: omap gpmc: Use dev_add_param_enum Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_omap_gpmc.c | 36 +++++++++---------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index 448e7b979..3053a8e89 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -90,7 +90,7 @@ int omap_gpmc_decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc); -static char *ecc_mode_strings[] = { +static const char *ecc_mode_strings[] = { "software", "hamming_hw_romcode", "bch4_hw", @@ -861,29 +861,11 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, return 0; } -static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, const char *val) +static int omap_gpmc_eccmode_set(struct param_d *param, void *priv) { - struct gpmc_nand_info *oinfo = dev->priv; - int i; + struct gpmc_nand_info *oinfo = priv; - if (!val) - return 0; - - for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) - if (!strcmp(ecc_mode_strings[i], val)) - break; - - if (i == ARRAY_SIZE(ecc_mode_strings)) { - dev_err(dev, "invalid ecc mode '%s'\n", val); - printf("valid modes:\n"); - for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) - printf("%s\n", ecc_mode_strings[i]); - return -EINVAL; - } - - dev_param_set_generic(dev, param, ecc_mode_strings[i]); - - return omap_gpmc_eccmode(oinfo, i); + return omap_gpmc_eccmode(oinfo, oinfo->ecc_mode); } static int gpmc_set_buswidth(struct nand_chip *chip, int buswidth) @@ -1062,11 +1044,13 @@ static int gpmc_nand_probe(struct device_d *pdev) nand->options |= NAND_SKIP_BBTSCAN; - dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0); - dev_set_param(pdev, "eccmode", ecc_mode_strings[pdata->ecc_mode]); + oinfo->ecc_mode = pdata->ecc_mode; - if (! IS_ENABLED(CONFIG_PARAMETER)) - omap_gpmc_eccmode(oinfo, pdata->ecc_mode); + dev_add_param_enum(pdev, "eccmode", + omap_gpmc_eccmode_set, NULL, (int *)&oinfo->ecc_mode, + ecc_mode_strings, ARRAY_SIZE(ecc_mode_strings), oinfo); + + omap_gpmc_eccmode(oinfo, oinfo->ecc_mode); /* We are all set to register with the system now! */ err = add_mtd_nand_device(minfo, "nand"); From 54f55d79d92986477c08791fe8cbdef32e3eae6f Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 25 Nov 2013 15:37:33 +0100 Subject: [PATCH 17/20] string: Add (x)memdup It's a common task to duplicate some memory. Add (x)memdup functions. Signed-off-by: Sascha Hauer --- include/string.h | 2 ++ include/xfuncs.h | 1 + lib/string.c | 14 ++++++++++++++ lib/xfuncs.c | 10 ++++++++++ 4 files changed, 27 insertions(+) diff --git a/include/string.h b/include/string.h index b906e1504..a833da131 100644 --- a/include/string.h +++ b/include/string.h @@ -3,4 +3,6 @@ #include +void *memdup(const void *, size_t); + #endif /* __STRING_H */ diff --git a/include/xfuncs.h b/include/xfuncs.h index 261aaa5c1..8efc99dbc 100644 --- a/include/xfuncs.h +++ b/include/xfuncs.h @@ -8,5 +8,6 @@ void *xrealloc(void *ptr, size_t size); void *xzalloc(size_t size); char *xstrdup(const char *s); void* xmemalign(size_t alignment, size_t bytes); +void* xmemdup(const void *orig, size_t size); #endif /* __XFUNCS_H */ diff --git a/lib/string.c b/lib/string.c index eeec137c9..ceced7f48 100644 --- a/lib/string.c +++ b/lib/string.c @@ -725,3 +725,17 @@ void *memchr_inv(const void *start, int c, size_t bytes) return check_bytes8(start, value, bytes % 8); } EXPORT_SYMBOL(memchr_inv); + +void *memdup(const void *orig, size_t size) +{ + void *buf; + + buf = malloc(size); + if (!buf) + return NULL; + + memcpy(buf, orig, size); + + return buf; +} +EXPORT_SYMBOL(memdup); diff --git a/lib/xfuncs.c b/lib/xfuncs.c index db8572019..86d001378 100644 --- a/lib/xfuncs.c +++ b/lib/xfuncs.c @@ -75,3 +75,13 @@ void* xmemalign(size_t alignment, size_t bytes) return p; } EXPORT_SYMBOL(xmemalign); + +void *xmemdup(const void *orig, size_t size) +{ + void *buf = xmalloc(size); + + memcpy(buf, orig, size); + + return buf; +} +EXPORT_SYMBOL(xmemdup); From f92f309c54996e0b4d09953820919161fbecac1d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 25 Nov 2013 15:39:11 +0100 Subject: [PATCH 18/20] device: Add functions to add resources We currently have functions to add a device based on function parameters. This adds the corresponding functions to add resources to a device without registering the device itself. This is useful to manipulate devices before registering them. Signed-off-by: Sascha Hauer --- drivers/base/resource.c | 60 ++++++++++++++++++++++++++++++++--------- include/driver.h | 9 +++++++ 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/drivers/base/resource.c b/drivers/base/resource.c index 2985c78e3..71ae0fe3c 100644 --- a/drivers/base/resource.c +++ b/drivers/base/resource.c @@ -20,33 +20,67 @@ #include #include #include +#include -static struct device_d *alloc_device(const char* devname, int id, void *pdata) +struct device_d *device_alloc(const char *devname, int id) { struct device_d *dev; dev = xzalloc(sizeof(*dev)); strcpy(dev->name, devname); dev->id = id; - dev->platform_data = pdata; return dev; } +int device_add_data(struct device_d *dev, void *data, size_t size) +{ + free(dev->platform_data); + + if (data) + dev->platform_data = xmemdup(data, size); + else + dev->platform_data = NULL; + + return 0; +} + +int device_add_resources(struct device_d *dev, const struct resource *res, int num) +{ + dev->resource = xmemdup(res, sizeof(*res) * num); + dev->num_resources = num; + + return 0; +} + +int device_add_resource(struct device_d *dev, const char *resname, + resource_size_t start, resource_size_t size, unsigned int flags) +{ + struct resource res = { + .start = start, + .end = start + size - 1, + .flags = flags, + }; + + if (resname) + res.name = xstrdup(resname); + + return device_add_resources(dev, &res, 1); +} + struct device_d *add_generic_device(const char* devname, int id, const char *resname, resource_size_t start, resource_size_t size, unsigned int flags, void *pdata) { - struct resource *res; + struct device_d *dev; - res = xzalloc(sizeof(struct resource)); - if (resname) - res[0].name = xstrdup(resname); - res[0].start = start; - res[0].end = start + size - 1; - res[0].flags = flags; + dev = device_alloc(devname, id); + dev->platform_data = pdata; + device_add_resource(dev, resname, start, size, flags); - return add_generic_device_res(devname, id, res, 1, pdata); + platform_device_register(dev); + + return dev; } EXPORT_SYMBOL(add_generic_device); @@ -55,9 +89,9 @@ struct device_d *add_generic_device_res(const char* devname, int id, { struct device_d *dev; - dev = alloc_device(devname, id, pdata); - dev->resource = res; - dev->num_resources = nb; + dev = device_alloc(devname, id); + dev->platform_data = pdata; + device_add_resources(dev, res, nb); platform_device_register(dev); diff --git a/include/driver.h b/include/driver.h index 7f0532e1a..bbe789b51 100644 --- a/include/driver.h +++ b/include/driver.h @@ -229,6 +229,15 @@ void *dev_get_mem_region(struct device_d *dev, int num); */ void __iomem *dev_request_mem_region(struct device_d *dev, int num); +struct device_d *device_alloc(const char *devname, int id); + +int device_add_resources(struct device_d *dev, const struct resource *res, int num); + +int device_add_resource(struct device_d *dev, const char *resname, + resource_size_t start, resource_size_t size, unsigned int flags); + +int device_add_data(struct device_d *dev, void *data, size_t size); + /* * register a generic device * with only one resource From 599e61ebd9b9b1f3d3fd2ed45af9ecee06c00ac3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 21 Nov 2013 09:17:41 +0100 Subject: [PATCH 19/20] bus: Add omap gpmc driver This adds a devicetree-only driver for to configure the gpmc and its child devices from dt. Currently only NAND is supported. Signed-off-by: Sascha Hauer --- arch/arm/mach-omap/include/mach/gpmc_nand.h | 3 + drivers/bus/Kconfig | 6 + drivers/bus/Makefile | 1 + drivers/bus/omap-gpmc.c | 523 ++++++++++++++++++++ 4 files changed, 533 insertions(+) create mode 100644 drivers/bus/omap-gpmc.c diff --git a/arch/arm/mach-omap/include/mach/gpmc_nand.h b/arch/arm/mach-omap/include/mach/gpmc_nand.h index 8d138ec9b..8839486fc 100644 --- a/arch/arm/mach-omap/include/mach/gpmc_nand.h +++ b/arch/arm/mach-omap/include/mach/gpmc_nand.h @@ -59,6 +59,9 @@ struct gpmc_nand_platform_data { struct nand_ecclayout *oob; /** gpmc config for nand */ struct gpmc_config *nand_cfg; + + struct device_node *of_node; + struct device_node *elm_of_node; }; int omap_add_gpmc_nand_device(struct gpmc_nand_platform_data *pdata); diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 5938d3f2d..b0d3c1fe9 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -4,4 +4,10 @@ config IMX_WEIM depends on ARCH_IMX bool "i.MX WEIM driver" +config BUS_OMAP_GPMC + depends on ARCH_OMAP + depends on OFDEVICE + depends on OMAP_GPMC + bool "TI OMAP/AM33xx GPMC support" + endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 42a8d4947..f1c5ac73c 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_IMX_WEIM) += imx-weim.o +obj-$(CONFIG_BUS_OMAP_GPMC) += omap-gpmc.o diff --git a/drivers/bus/omap-gpmc.c b/drivers/bus/omap-gpmc.c new file mode 100644 index 000000000..ad21af290 --- /dev/null +++ b/drivers/bus/omap-gpmc.c @@ -0,0 +1,523 @@ +/* + * OMAP GPMC driver. Based upon the corresponding Linux Code + * + * Copyright (C) 2013 Sascha Hauer, Pengutronix, + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPMC_CS_NUM 8 +#define GPMC_NR_WAITPINS 4 + +#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) +#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30) +#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29) +#define GPMC_CONFIG1_READTYPE_SYNC (1 << 29) +#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28) +#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27) +#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27) +#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25) +#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) +#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) +#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) +#define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18) +#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) +#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) +#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) +#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10) +#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) +#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8) +#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4) +#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3) +#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) +#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2)) +#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3)) +#define GPMC_CONFIG2_CSEXTRADELAY (1 << 7) +#define GPMC_CONFIG3_ADVEXTRADELAY (1 << 7) +#define GPMC_CONFIG4_OEEXTRADELAY (1 << 7) +#define GPMC_CONFIG4_WEEXTRADELAY (1 << 23) +#define GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN (1 << 6) +#define GPMC_CONFIG6_CYCLE2CYCLESAMECSEN (1 << 7) +#define GPMC_CONFIG7_CSVALID (1 << 6) + +static unsigned int gpmc_cs_num = GPMC_CS_NUM; +static unsigned int gpmc_nr_waitpins; +static unsigned long gpmc_l3_clk = 100000000; /* This should be a proper clock */ +static void __iomem *gpmc_base; + +/* bool type time settings */ +struct gpmc_bool_timings { + bool cycle2cyclediffcsen; + bool cycle2cyclesamecsen; + bool we_extra_delay; + bool oe_extra_delay; + bool adv_extra_delay; + bool cs_extra_delay; + bool time_para_granularity; +}; + +/* + * Note that all values in this struct are in nanoseconds except sync_clk + * (which is in picoseconds), while the register values are in gpmc_fck cycles. + */ +struct gpmc_timings { + /* Minimum clock period for synchronous mode (in picoseconds) */ + u32 sync_clk; + + /* Chip-select signal timings corresponding to 1 */ + u32 cs_on; /* Assertion time */ + u32 cs_rd_off; /* Read deassertion time */ + u32 cs_wr_off; /* Write deassertion time */ + + /* ADV signal timings corresponding to GPMC_CONFIG3 */ + u32 adv_on; /* Assertion time */ + u32 adv_rd_off; /* Read deassertion time */ + u32 adv_wr_off; /* Write deassertion time */ + + /* WE signals timings corresponding to GPMC_CONFIG4 */ + u32 we_on; /* WE assertion time */ + u32 we_off; /* WE deassertion time */ + + /* OE signals timings corresponding to GPMC_CONFIG4 */ + u32 oe_on; /* OE assertion time */ + u32 oe_off; /* OE deassertion time */ + + /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ + u32 page_burst_access; /* Multiple access word delay */ + u32 access; /* Start-cycle to first data valid delay */ + u32 rd_cycle; /* Total read cycle time */ + u32 wr_cycle; /* Total write cycle time */ + + u32 bus_turnaround; + u32 cycle2cycle_delay; + + u32 wait_monitoring; + u32 clk_activation; + + /* The following are only on OMAP3430 */ + u32 wr_access; /* WRACCESSTIME */ + u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ + + struct gpmc_bool_timings bool_timings; +}; + +struct gpmc_settings { + bool burst_wrap; /* enables wrap bursting */ + bool burst_read; /* enables read page/burst mode */ + bool burst_write; /* enables write page/burst mode */ + bool device_nand; /* device is NAND */ + bool sync_read; /* enables synchronous reads */ + bool sync_write; /* enables synchronous writes */ + bool wait_on_read; /* monitor wait on reads */ + bool wait_on_write; /* monitor wait on writes */ + u32 burst_len; /* page/burst length */ + u32 device_width; /* device bus width (8 or 16 bit) */ + u32 mux_add_data; /* multiplex address & data */ + u32 wait_pin; /* wait-pin to be used */ +}; + +struct imx_gpmc { + struct device_d *dev; + void __iomem *base; + struct imx_gpmc_devtype *devtype; +}; + +static void gpmc_cs_bool_timings(struct gpmc_config *gpmc_config, const struct gpmc_bool_timings *p) +{ + if (p->time_para_granularity) + gpmc_config->cfg[0] |= GPMC_CONFIG1_TIME_PARA_GRAN; + + if (p->cs_extra_delay) + gpmc_config->cfg[1] |= GPMC_CONFIG2_CSEXTRADELAY; + if (p->adv_extra_delay) + gpmc_config->cfg[2] |= GPMC_CONFIG3_ADVEXTRADELAY; + if (p->oe_extra_delay) + gpmc_config->cfg[3] |= GPMC_CONFIG4_OEEXTRADELAY; + if (p->we_extra_delay) + gpmc_config->cfg[3] |= GPMC_CONFIG4_OEEXTRADELAY; + if (p->cycle2cyclesamecsen) + gpmc_config->cfg[5] |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN; + if (p->cycle2cyclediffcsen) + gpmc_config->cfg[5] |= GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN; +} + +static unsigned long gpmc_get_fclk_period(void) +{ + unsigned long rate = gpmc_l3_clk; + + rate /= 1000; + rate = 1000000000 / rate; /* In picoseconds */ + + return rate; +} + +static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) +{ + unsigned long tick_ps; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = gpmc_get_fclk_period(); + + return (time_ns * 1000 + tick_ps - 1) / tick_ps; +} + +int gpmc_calc_divider(unsigned int sync_clk) +{ + int div; + u32 l; + + l = sync_clk + (gpmc_get_fclk_period() - 1); + div = l / gpmc_get_fclk_period(); + if (div > 4) + return -1; + if (div <= 0) + div = 1; + + return div; +} + +static int set_cfg(struct gpmc_config *gpmc_config, int reg, + int st_bit, int end_bit, int time) +{ + int ticks, mask, nr_bits; + + if (time == 0) + ticks = 0; + else + ticks = gpmc_ns_to_ticks(time); + + nr_bits = end_bit - st_bit + 1; + if (ticks >= 1 << nr_bits) + return -EINVAL; + + mask = (1 << nr_bits) - 1; + gpmc_config->cfg[reg] &= ~(mask << st_bit); + gpmc_config->cfg[reg] |= ticks << st_bit; + + return 0; +} + +static int gpmc_timings_to_config(struct gpmc_config *gpmc_config, const struct gpmc_timings *t) +{ + int div, ret = 0; + + div = gpmc_calc_divider(t->sync_clk); + if (div < 0) + return div; + + ret |= set_cfg(gpmc_config, 0, 18, 19, t->wait_monitoring); + ret |= set_cfg(gpmc_config, 0, 25, 26, t->clk_activation); + + ret |= set_cfg(gpmc_config, 1, 0, 3, t->cs_on); + ret |= set_cfg(gpmc_config, 1, 8, 12, t->cs_rd_off); + ret |= set_cfg(gpmc_config, 1, 16, 20, t->cs_wr_off); + + ret |= set_cfg(gpmc_config, 2, 0, 3, t->adv_on); + ret |= set_cfg(gpmc_config, 2, 8, 12, t->adv_rd_off); + ret |= set_cfg(gpmc_config, 2, 16, 20, t->adv_wr_off); + + ret |= set_cfg(gpmc_config, 3, 0, 3, t->oe_on); + ret |= set_cfg(gpmc_config, 3, 8, 12, t->oe_off); + ret |= set_cfg(gpmc_config, 3, 16, 19, t->we_on); + ret |= set_cfg(gpmc_config, 3, 24, 28, t->we_off); + + ret |= set_cfg(gpmc_config, 4, 0, 4, t->rd_cycle); + ret |= set_cfg(gpmc_config, 4, 8, 12, t->wr_cycle); + ret |= set_cfg(gpmc_config, 4, 16, 20, t->access); + + ret |= set_cfg(gpmc_config, 4, 24, 27, t->page_burst_access); + + ret |= set_cfg(gpmc_config, 5, 0, 3, t->bus_turnaround); + ret |= set_cfg(gpmc_config, 5, 8, 11, t->cycle2cycle_delay); + ret |= set_cfg(gpmc_config, 5, 16, 19, t->wr_data_mux_bus); + ret |= set_cfg(gpmc_config, 5, 24, 28, t->wr_access); + + if (ret) + return ret; + + gpmc_cs_bool_timings(gpmc_config, &t->bool_timings); + + return 0; +} + +/** + * gpmc_read_settings_dt - read gpmc settings from device-tree + * @np: pointer to device-tree node for a gpmc child device + * @p: pointer to gpmc settings structure + * + * Reads the GPMC settings for a GPMC child device from device-tree and + * stores them in the GPMC settings structure passed. The GPMC settings + * structure is initialised to zero by this function and so any + * previously stored settings will be cleared. + */ +static void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p) +{ + memset(p, 0, sizeof(struct gpmc_settings)); + + p->sync_read = of_property_read_bool(np, "gpmc,sync-read"); + p->sync_write = of_property_read_bool(np, "gpmc,sync-write"); + of_property_read_u32(np, "gpmc,device-width", &p->device_width); + of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data); + + if (!of_property_read_u32(np, "gpmc,burst-length", &p->burst_len)) { + p->burst_wrap = of_property_read_bool(np, "gpmc,burst-wrap"); + p->burst_read = of_property_read_bool(np, "gpmc,burst-read"); + p->burst_write = of_property_read_bool(np, "gpmc,burst-write"); + if (!p->burst_read && !p->burst_write) + pr_warn("%s: page/burst-length set but not used!\n", + __func__); + } + + if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) { + p->wait_on_read = of_property_read_bool(np, + "gpmc,wait-on-read"); + p->wait_on_write = of_property_read_bool(np, + "gpmc,wait-on-write"); + if (!p->wait_on_read && !p->wait_on_write) + pr_warn("%s: read/write wait monitoring not enabled!\n", + __func__); + } +} + +static void gpmc_read_timings_dt(struct device_node *np, + struct gpmc_timings *gpmc_t) +{ + struct gpmc_bool_timings *p; + + if (!np || !gpmc_t) + return; + + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + /* minimum clock period for syncronous mode */ + of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk); + + /* chip select timtings */ + of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on); + of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off); + of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off); + + /* ADV signal timings */ + of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on); + of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off); + of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off); + + /* WE signal timings */ + of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on); + of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off); + + /* OE signal timings */ + of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on); + of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off); + + /* access and cycle timings */ + of_property_read_u32(np, "gpmc,page-burst-access-ns", + &gpmc_t->page_burst_access); + of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access); + of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle); + of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle); + of_property_read_u32(np, "gpmc,bus-turnaround-ns", + &gpmc_t->bus_turnaround); + of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns", + &gpmc_t->cycle2cycle_delay); + of_property_read_u32(np, "gpmc,wait-monitoring-ns", + &gpmc_t->wait_monitoring); + of_property_read_u32(np, "gpmc,clk-activation-ns", + &gpmc_t->clk_activation); + + /* only applicable to OMAP3+ */ + of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access); + of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns", + &gpmc_t->wr_data_mux_bus); + + /* bool timing parameters */ + p = &gpmc_t->bool_timings; + + p->cycle2cyclediffcsen = + of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen"); + p->cycle2cyclesamecsen = + of_property_read_bool(np, "gpmc,cycle2cycle-samecsen"); + p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay"); + p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay"); + p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay"); + p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay"); + p->time_para_granularity = + of_property_read_bool(np, "gpmc,time-para-granularity"); +} + +struct dt_eccmode { + const char *name; + enum gpmc_ecc_mode mode; +}; + +static struct dt_eccmode modes[] = { + { + .name = "ham1", + .mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE, + }, { + .name = "sw", + .mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE, + }, { + .name = "hw", + .mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE, + }, { + .name = "hw-romcode", + .mode = OMAP_ECC_HAMMING_CODE_HW_ROMCODE, + }, { + .name = "bch4", + .mode = OMAP_ECC_BCH4_CODE_HW, + }, { + .name = "bch8", + .mode = OMAP_ECC_BCH8_CODE_HW, + }, { + .name = "bch8-romcode", + .mode = OMAP_ECC_BCH8_CODE_HW_ROMCODE, + }, +}; + +static int gpmc_probe_nand_child(struct device_d *dev, + struct device_node *child) +{ + u32 val; + const char *s; + struct gpmc_timings gpmc_t; + struct gpmc_nand_platform_data gpmc_nand_data = {}; + struct resource res; + struct gpmc_settings gpmc_settings = {}; + int ret, i; + + if (of_property_read_u32(child, "reg", &val) < 0) { + dev_err(dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + gpmc_base = dev_get_mem_region(dev, 0); + if (!gpmc_base) + return -ENODEV; + + gpmc_nand_data.cs = val; + gpmc_nand_data.of_node = child; + + /* Detect availability of ELM module */ + gpmc_nand_data.elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); + if (gpmc_nand_data.elm_of_node == NULL) + gpmc_nand_data.elm_of_node = + of_parse_phandle(child, "elm_id", 0); + if (gpmc_nand_data.elm_of_node == NULL) + pr_warn("%s: ti,elm-id property not found\n", __func__); + + /* select ecc-scheme for NAND */ + if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { + pr_err("%s: ti,nand-ecc-opt not found\n", __func__); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + struct dt_eccmode *mode = &modes[i]; + if (!strcmp(s, mode->name)) + gpmc_nand_data.ecc_mode = mode->mode; + } + + val = of_get_nand_bus_width(child); + if (val == 16) + gpmc_nand_data.device_width = NAND_BUSWIDTH_16; + + gpmc_read_timings_dt(child, &gpmc_t); + gpmc_read_settings_dt(child, &gpmc_settings); + + gpmc_nand_data.wait_mon_pin = gpmc_settings.wait_pin; + + gpmc_nand_data.nand_cfg = xzalloc(sizeof(*gpmc_nand_data.nand_cfg)); + + gpmc_timings_to_config(gpmc_nand_data.nand_cfg, &gpmc_t); + + gpmc_nand_data.nand_cfg->cfg[0] |= 2 << 10; + + ret = of_address_to_resource(child, 0, &res); + if (ret) + pr_err("of_address_to_resource failed\n"); + + gpmc_nand_data.nand_cfg->base = res.start; + gpmc_nand_data.nand_cfg->size = GPMC_SIZE_16M; + + gpmc_cs_config(gpmc_nand_data.cs, gpmc_nand_data.nand_cfg); + + dev = device_alloc("gpmc_nand", DEVICE_ID_DYNAMIC); + device_add_resource(dev, NULL, (resource_size_t)gpmc_base, SZ_4K, IORESOURCE_MEM); + device_add_data(dev, &gpmc_nand_data, sizeof(gpmc_nand_data)); + dev->device_node = child; + platform_device_register(dev); + + return 0; +} + +static int gpmc_probe(struct device_d *dev) +{ + struct device_node *child, *node = dev->device_node; + int ret; + + gpmc_generic_init(0x12); + + ret = of_property_read_u32(node, "gpmc,num-cs", + &gpmc_cs_num); + if (ret < 0) + return ret; + + ret = of_property_read_u32(node, "gpmc,num-waitpins", + &gpmc_nr_waitpins); + if (ret < 0) + return ret; + + for_each_child_of_node(node, child) { + + if (!child->name) + continue; + + if (!strncmp(child->name, "nand", 4)) + ret = gpmc_probe_nand_child(dev, child); + else + dev_warn(dev, "unhandled child %s\n", child->name); + + if (ret) + break; + } + + if (ret) + goto gpmc_err; + + return 0; + +gpmc_err: + return ret; +} + +static struct of_device_id gpmc_id_table[] = { + { .compatible = "ti,omap2420-gpmc" }, + { .compatible = "ti,omap2430-gpmc" }, + { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ + { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ + { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ + { } +}; + +static struct driver_d gpmc_driver = { + .name = "omap-gpmc", + .of_compatible = DRV_OF_COMPAT(gpmc_id_table), + .probe = gpmc_probe, +}; +device_platform_driver(gpmc_driver); From 03d601d58f4d6228bcb67e6035d4ede7b1d99ae3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 20 Nov 2013 07:18:37 +0100 Subject: [PATCH 20/20] mmc: omap: Add devicetree support Signed-off-by: Sascha Hauer --- drivers/mci/omap_hsmmc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c index 19db461e0..2e736e13d 100644 --- a/drivers/mci/omap_hsmmc.c +++ b/drivers/mci/omap_hsmmc.c @@ -640,9 +640,22 @@ static struct platform_device_id omap_mmc_ids[] = { }, }; +static __maybe_unused struct of_device_id omap_mmc_dt_ids[] = { + { + .compatible = "ti,omap3-hsmmc", + .data = (unsigned long)&omap3_data, + }, { + .compatible = "ti,omap4-hsmmc", + .data = (unsigned long)&omap4_data, + }, { + /* sentinel */ + } +}; + static struct driver_d omap_mmc_driver = { .name = "omap-hsmmc", .probe = omap_mmc_probe, .id_table = omap_mmc_ids, + .of_compatible = DRV_OF_COMPAT(omap_mmc_dt_ids), }; device_platform_driver(omap_mmc_driver);