diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/gpio-tegra.c | 143 | ||||
-rw-r--r-- | drivers/mmc/host/davinci_mmc.c | 13 | ||||
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 239 |
3 files changed, 328 insertions, 67 deletions
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 6b65207c8106..61044c889f7f 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -20,10 +20,11 @@ #include <linux/init.h> #include <linux/irq.h> #include <linux/interrupt.h> - #include <linux/io.h> #include <linux/gpio.h> #include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/module.h> #include <asm/mach/irq.h> @@ -35,9 +36,7 @@ #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) -#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \ - GPIO_BANK(x) * 0x80 + \ - GPIO_PORT(x) * 4) +#define GPIO_REG(x) (GPIO_BANK(x) * 0x80 + GPIO_PORT(x) * 4) #define GPIO_CNF(x) (GPIO_REG(x) + 0x00) #define GPIO_OE(x) (GPIO_REG(x) + 0x10) @@ -76,15 +75,18 @@ struct tegra_gpio_bank { }; -static struct tegra_gpio_bank tegra_gpio_banks[] = { - {.bank = 0, .irq = INT_GPIO1}, - {.bank = 1, .irq = INT_GPIO2}, - {.bank = 2, .irq = INT_GPIO3}, - {.bank = 3, .irq = INT_GPIO4}, - {.bank = 4, .irq = INT_GPIO5}, - {.bank = 5, .irq = INT_GPIO6}, - {.bank = 6, .irq = INT_GPIO7}, -}; +static void __iomem *regs; +static struct tegra_gpio_bank tegra_gpio_banks[7]; + +static inline void tegra_gpio_writel(u32 val, u32 reg) +{ + __raw_writel(val, regs + reg); +} + +static inline u32 tegra_gpio_readl(u32 reg) +{ + return __raw_readl(regs + reg); +} static int tegra_gpio_compose(int bank, int port, int bit) { @@ -98,7 +100,7 @@ static void tegra_gpio_mask_write(u32 reg, int gpio, int value) val = 0x100 << GPIO_BIT(gpio); if (value) val |= 1 << GPIO_BIT(gpio); - __raw_writel(val, reg); + tegra_gpio_writel(val, reg); } void tegra_gpio_enable(int gpio) @@ -118,7 +120,7 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) { - return (__raw_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; } static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -155,7 +157,7 @@ static void tegra_gpio_irq_ack(struct irq_data *d) { int gpio = d->irq - INT_GPIO_BASE; - __raw_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); + tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); } static void tegra_gpio_irq_mask(struct irq_data *d) @@ -208,10 +210,10 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) spin_lock_irqsave(&bank->lvl_lock[port], flags); - val = __raw_readl(GPIO_INT_LVL(gpio)); + val = tegra_gpio_readl(GPIO_INT_LVL(gpio)); val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); val |= lvl_type << GPIO_BIT(gpio); - __raw_writel(val, GPIO_INT_LVL(gpio)); + tegra_gpio_writel(val, GPIO_INT_LVL(gpio)); spin_unlock_irqrestore(&bank->lvl_lock[port], flags); @@ -237,12 +239,12 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) for (port = 0; port < 4; port++) { int gpio = tegra_gpio_compose(bank->bank, port, 0); - unsigned long sta = __raw_readl(GPIO_INT_STA(gpio)) & - __raw_readl(GPIO_INT_ENB(gpio)); - u32 lvl = __raw_readl(GPIO_INT_LVL(gpio)); + unsigned long sta = tegra_gpio_readl(GPIO_INT_STA(gpio)) & + tegra_gpio_readl(GPIO_INT_ENB(gpio)); + u32 lvl = tegra_gpio_readl(GPIO_INT_LVL(gpio)); for_each_set_bit(pin, &sta, 8) { - __raw_writel(1 << pin, GPIO_INT_CLR(gpio)); + tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio)); /* if gpio is edge triggered, clear condition * before executing the hander so that we don't @@ -276,11 +278,11 @@ void tegra_gpio_resume(void) for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { unsigned int gpio = (b<<5) | (p<<3); - __raw_writel(bank->cnf[p], GPIO_CNF(gpio)); - __raw_writel(bank->out[p], GPIO_OUT(gpio)); - __raw_writel(bank->oe[p], GPIO_OE(gpio)); - __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); - __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); + tegra_gpio_writel(bank->cnf[p], GPIO_CNF(gpio)); + tegra_gpio_writel(bank->out[p], GPIO_OUT(gpio)); + tegra_gpio_writel(bank->oe[p], GPIO_OE(gpio)); + tegra_gpio_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); + tegra_gpio_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); } } @@ -299,11 +301,11 @@ void tegra_gpio_suspend(void) for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { unsigned int gpio = (b<<5) | (p<<3); - bank->cnf[p] = __raw_readl(GPIO_CNF(gpio)); - bank->out[p] = __raw_readl(GPIO_OUT(gpio)); - bank->oe[p] = __raw_readl(GPIO_OE(gpio)); - bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio)); - bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio)); + bank->cnf[p] = tegra_gpio_readl(GPIO_CNF(gpio)); + bank->out[p] = tegra_gpio_readl(GPIO_OUT(gpio)); + bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio)); + bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio)); + bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio)); } } local_irq_restore(flags); @@ -333,28 +335,55 @@ static struct irq_chip tegra_gpio_irq_chip = { */ static struct lock_class_key gpio_lock_class; -static int __init tegra_gpio_init(void) +static int __devinit tegra_gpio_probe(struct platform_device *pdev) { + struct resource *res; struct tegra_gpio_bank *bank; int gpio; int i; int j; + for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) { + dev_err(&pdev->dev, "Missing IRQ resource\n"); + return -ENODEV; + } + + bank = &tegra_gpio_banks[i]; + bank->bank = i; + bank->irq = res->start; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "Couldn't request MEM resource\n"); + return -ENODEV; + } + + regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); - __raw_writel(0x00, GPIO_INT_ENB(gpio)); + tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio)); } } #ifdef CONFIG_OF_GPIO - /* - * This isn't ideal, but it gets things hooked up until this - * driver is converted into a platform_device - */ - tegra_gpio_chip.of_node = of_find_compatible_node(NULL, NULL, - "nvidia,tegra20-gpio"); -#endif /* CONFIG_OF_GPIO */ + tegra_gpio_chip.of_node = pdev->dev.of_node; +#endif gpiochip_add(&tegra_gpio_chip); @@ -384,6 +413,24 @@ static int __init tegra_gpio_init(void) return 0; } +static struct of_device_id tegra_gpio_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-gpio", }, + { }, +}; + +static struct platform_driver tegra_gpio_driver = { + .driver = { + .name = "tegra-gpio", + .owner = THIS_MODULE, + .of_match_table = tegra_gpio_of_match, + }, + .probe = tegra_gpio_probe, +}; + +static int __init tegra_gpio_init(void) +{ + return platform_driver_register(&tegra_gpio_driver); +} postcore_initcall(tegra_gpio_init); void __init tegra_gpio_config(struct tegra_gpio_table *table, int num) @@ -416,13 +463,13 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", i, j, - __raw_readl(GPIO_CNF(gpio)), - __raw_readl(GPIO_OE(gpio)), - __raw_readl(GPIO_OUT(gpio)), - __raw_readl(GPIO_IN(gpio)), - __raw_readl(GPIO_INT_STA(gpio)), - __raw_readl(GPIO_INT_ENB(gpio)), - __raw_readl(GPIO_INT_LVL(gpio))); + tegra_gpio_readl(GPIO_CNF(gpio)), + tegra_gpio_readl(GPIO_OE(gpio)), + tegra_gpio_readl(GPIO_OUT(gpio)), + tegra_gpio_readl(GPIO_IN(gpio)), + tegra_gpio_readl(GPIO_INT_STA(gpio)), + tegra_gpio_readl(GPIO_INT_ENB(gpio)), + tegra_gpio_readl(GPIO_INT_LVL(gpio))); } } return 0; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 0076c7448fe6..64a8325a4a8a 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_davinci_host *host = mmc_priv(mmc); + struct platform_device *pdev = to_platform_device(mmc->parent); + struct davinci_mmc_config *config = pdev->dev.platform_data; dev_dbg(mmc_dev(host->mmc), "clock %dHz busmode %d powermode %d Vdd %04x\n", ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + switch (ios->power_mode) { + case MMC_POWER_OFF: + if (config && config->set_power) + config->set_power(pdev->id, false); + break; + case MMC_POWER_UP: + if (config && config->set_power) + config->set_power(pdev->id, true); + break; + } + switch (ios->bus_width) { case MMC_BUS_WIDTH_8: dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n"); diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 944291e10f97..ba3a46b78b75 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -35,8 +35,7 @@ extern int usb_disabled(void); static void at91_start_clock(void) { - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) - clk_enable(hclk); + clk_enable(hclk); clk_enable(iclk); clk_enable(fclk); clocked = 1; @@ -46,8 +45,7 @@ static void at91_stop_clock(void) { clk_disable(fclk); clk_disable(iclk); - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) - clk_disable(hclk); + clk_disable(hclk); clocked = 0; } @@ -142,8 +140,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) - hclk = clk_get(&pdev->dev, "hck0"); + hclk = clk_get(&pdev->dev, "hclk"); at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -155,8 +152,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* Error handling */ at91_stop_hc(pdev); - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) - clk_put(hclk); + clk_put(hclk); clk_put(fclk); clk_put(iclk); @@ -192,8 +188,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) - clk_put(hclk); + clk_put(hclk); clk_put(fclk); clk_put(iclk); fclk = iclk = hclk = NULL; @@ -223,6 +218,156 @@ ohci_at91_start (struct usb_hcd *hcd) return 0; } +static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) +{ + if (port < 0 || port >= 2) + return; + + gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); +} + +static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) +{ + if (port < 0 || port >= 2) + return -EINVAL; + + return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; +} + +/* + * Update the status data from the hub with the over-current indicator change. + */ +static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct at91_usbh_data *pdata = hcd->self.controller->platform_data; + int length = ohci_hub_status_data(hcd, buf); + int port; + + for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { + if (pdata->overcurrent_changed[port]) { + if (! length) + length = 1; + buf[0] |= 1 << (port + 1); + } + } + + return length; +} + +/* + * Look at the control requests to the root hub and see if we need to override. + */ +static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct at91_usbh_data *pdata = hcd->self.controller->platform_data; + struct usb_hub_descriptor *desc; + int ret = -EINVAL; + u32 *data = (u32 *)buf; + + dev_dbg(hcd->self.controller, + "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", + hcd, typeReq, wValue, wIndex, buf, wLength); + + switch (typeReq) { + case SetPortFeature: + if (wValue == USB_PORT_FEAT_POWER) { + dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); + ohci_at91_usb_set_power(pdata, wIndex - 1, 1); + goto out; + } + break; + + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_C_OVER_CURRENT: + dev_dbg(hcd->self.controller, + "ClearPortFeature: C_OVER_CURRENT\n"); + + if (wIndex == 1 || wIndex == 2) { + pdata->overcurrent_changed[wIndex-1] = 0; + pdata->overcurrent_status[wIndex-1] = 0; + } + + goto out; + + case USB_PORT_FEAT_OVER_CURRENT: + dev_dbg(hcd->self.controller, + "ClearPortFeature: OVER_CURRENT\n"); + + if (wIndex == 1 || wIndex == 2) { + pdata->overcurrent_status[wIndex-1] = 0; + } + + goto out; + + case USB_PORT_FEAT_POWER: + dev_dbg(hcd->self.controller, + "ClearPortFeature: POWER\n"); + + if (wIndex == 1 || wIndex == 2) { + ohci_at91_usb_set_power(pdata, wIndex - 1, 0); + return 0; + } + } + break; + } + + ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + if (ret) + goto out; + + switch (typeReq) { + case GetHubDescriptor: + + /* update the hub's descriptor */ + + desc = (struct usb_hub_descriptor *)buf; + + dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n", + desc->wHubCharacteristics); + + /* remove the old configurations for power-switching, and + * over-current protection, and insert our new configuration + */ + + desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM); + desc->wHubCharacteristics |= cpu_to_le16(0x0001); + + if (pdata->overcurrent_supported) { + desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); + desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); + } + + dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", + desc->wHubCharacteristics); + + return ret; + + case GetPortStatus: + /* check port status */ + + dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); + + if (wIndex == 1 || wIndex == 2) { + if (! ohci_at91_usb_get_power(pdata, wIndex-1)) { + *data &= ~cpu_to_le32(RH_PS_PPS); + } + + if (pdata->overcurrent_changed[wIndex-1]) { + *data |= cpu_to_le32(RH_PS_OCIC); + } + + if (pdata->overcurrent_status[wIndex-1]) { + *data |= cpu_to_le32(RH_PS_POCI); + } + } + } + + out: + return ret; +} + /*-------------------------------------------------------------------------*/ static const struct hc_driver ohci_at91_hc_driver = { @@ -258,8 +403,8 @@ static const struct hc_driver ohci_at91_hc_driver = { /* * root hub support */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, + .hub_status_data = ohci_at91_hub_status_data, + .hub_control = ohci_at91_hub_control, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, @@ -269,22 +414,71 @@ static const struct hc_driver ohci_at91_hc_driver = { /*-------------------------------------------------------------------------*/ +static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct at91_usbh_data *pdata = pdev->dev.platform_data; + int val, gpio, port; + + /* From the GPIO notifying the over-current situation, find + * out the corresponding port */ + gpio = irq_to_gpio(irq); + for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { + if (pdata->overcurrent_pin[port] == gpio) + break; + } + + if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { + dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); + return IRQ_HANDLED; + } + + val = gpio_get_value(gpio); + + /* When notified of an over-current situation, disable power + on the corresponding port, and mark this port in + over-current. */ + if (! val) { + ohci_at91_usb_set_power(pdata, port, 0); + pdata->overcurrent_status[port] = 1; + pdata->overcurrent_changed[port] = 1; + } + + dev_dbg(& pdev->dev, "overcurrent situation %s\n", + val ? "exited" : "notified"); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) { struct at91_usbh_data *pdata = pdev->dev.platform_data; int i; if (pdata) { - /* REVISIT make the driver support per-port power switching, - * and also overcurrent detection. Here we assume the ports - * are always powered while this driver is active, and use - * active-low power switches. - */ for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { if (pdata->vbus_pin[i] <= 0) continue; gpio_request(pdata->vbus_pin[i], "ohci_vbus"); - gpio_direction_output(pdata->vbus_pin[i], 0); + ohci_at91_usb_set_power(pdata, i, 1); + } + + for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { + int ret; + + if (pdata->overcurrent_pin[i] <= 0) + continue; + gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); + + ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]), + ohci_hcd_at91_overcurrent_irq, + IRQF_SHARED, "ohci_overcurrent", pdev); + if (ret) { + gpio_free(pdata->overcurrent_pin[i]); + dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); + } } } @@ -301,9 +495,16 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { if (pdata->vbus_pin[i] <= 0) continue; - gpio_direction_output(pdata->vbus_pin[i], 1); + ohci_at91_usb_set_power(pdata, i, 0); gpio_free(pdata->vbus_pin[i]); } + + for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { + if (pdata->overcurrent_pin[i] <= 0) + continue; + free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); + gpio_free(pdata->overcurrent_pin[i]); + } } device_init_wakeup(&pdev->dev, 0); |