mirror of https://github.com/OpenIPC/firmware.git
737 lines
17 KiB
Diff
737 lines
17 KiB
Diff
diff -drupN a/drivers/w1/masters/sunxi-owc.c b/drivers/w1/masters/sunxi-owc.c
|
|
--- a/drivers/w1/masters/sunxi-owc.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/w1/masters/sunxi-owc.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -0,0 +1,732 @@
|
|
+/*
|
|
+ * drivers/char/sunxi-owc/sunxi-owc.c
|
|
+ *
|
|
+ * Copyright (C) 2016-2020 Allwinner.
|
|
+ * lihuaxing <lihuaxing@allwinnertech.com>
|
|
+ *
|
|
+ * SUNXI OWC Controller Driver
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of
|
|
+ * the License, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/cdev.h>
|
|
+#include <linux/major.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <asm/irq.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/sched.h>
|
|
+
|
|
+#include <asm/io.h>
|
|
+
|
|
+#include "sunxi-owc.h"
|
|
+#include "../w1.h"
|
|
+#include "../w1_int.h"
|
|
+
|
|
+/* ==================== For debug =============================== */
|
|
+#define OWC_ENTER() pr_info("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
|
|
+#define OWC_EXIT() pr_info("%s()%d - %s\n", __func__, __LINE__, "Exit")
|
|
+#define OWC_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
+#define OWC_INFO(fmt, arg...) pr_info("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
+#define OWC_WARN(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
+#define OWC_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
+
|
|
+static struct sunxi_owc *powc;
|
|
+/* clear control and status register */
|
|
+
|
|
+static void owc_standard_setting(void __iomem *base_addr)
|
|
+{
|
|
+ /* select standard mode */
|
|
+ u32 reg_val = readl(base_addr + OW_CTL);
|
|
+ reg_val &= ~(0x01 << 1);
|
|
+ writel(reg_val, base_addr + OW_CTL);
|
|
+
|
|
+ /* timing control register setting */
|
|
+ reg_val = 0;
|
|
+ reg_val |= (TSU & 0x3) << 29;
|
|
+ reg_val |= (TREC & 0xF) << 24;
|
|
+ reg_val |= (TRDV & 0x1F) << 18;
|
|
+ reg_val |= (TLOW0 & 0x7F) << 11;
|
|
+ reg_val |= (TLOW1 & 0xF) << 7;
|
|
+ reg_val |= (TSLOT & 0x7F) << 0;
|
|
+ writel(reg_val, base_addr + SM_WR_RD_TCTL);
|
|
+
|
|
+ /* reset presence timing control setting */
|
|
+ reg_val = 0;
|
|
+ reg_val |= (TPDL & 0xFF) << 24;
|
|
+ reg_val |= (TPDH & 0x3F) << 18;
|
|
+ reg_val |= (TRSTL & 0x1FF) << 9;
|
|
+ reg_val |= (TRSTH & 0x1FF) << 0;
|
|
+ writel(reg_val, base_addr + SM_RST_PRESENCE_TCTL);
|
|
+}
|
|
+
|
|
+static void owc_enable_irq(void __iomem *base_addr, u32 bitmap)
|
|
+{
|
|
+ u32 reg_val = readl(base_addr + OW_INT_CTL);
|
|
+
|
|
+ reg_val |= bitmap;
|
|
+ writel(reg_val, base_addr + OW_INT_CTL);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void owc_disable_irq(void __iomem *base_addr, u32 bitmap)
|
|
+{
|
|
+ u32 reg_val = readl(base_addr + OW_INT_CTL);
|
|
+
|
|
+ reg_val &= ~bitmap;
|
|
+ writel(reg_val, base_addr + OW_INT_CTL);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void owc_config(void __iomem *base_addr)
|
|
+{
|
|
+ u32 reg_val = readl(base_addr + OW_CTL);
|
|
+
|
|
+ reg_val |= 1 << 0;
|
|
+ reg_val |= 1 << 9;
|
|
+ writel(reg_val, base_addr + OW_CTL);
|
|
+}
|
|
+
|
|
+static void owc_fclk(void __iomem *base_addr, u32 fclk)
|
|
+{
|
|
+ u32 reg_val = readl(base_addr + OW_FCLK);
|
|
+
|
|
+ reg_val &= ~OW_FCLK_MASK;
|
|
+ reg_val |= (fclk & 0x1F) << 16;
|
|
+ writel(reg_val, base_addr + OW_FCLK);
|
|
+}
|
|
+
|
|
+static void sunxi_owc_setup(struct sunxi_owc *powc)
|
|
+{
|
|
+ /* set simple mode, enable inner pull-up */
|
|
+ owc_config(powc->reg_base);
|
|
+
|
|
+ /* set ow_fclk */
|
|
+ powc->func_clk = powc->clk_freq/1000000;
|
|
+ owc_fclk(powc->reg_base, powc->func_clk);
|
|
+
|
|
+ /* set standrad mode timing */
|
|
+ owc_standard_setting(powc->reg_base);
|
|
+
|
|
+ /* enable standard_mode irq */
|
|
+ owc_enable_irq(powc->reg_base, INTEN_DGCH|INTEN_ST_CRC_FINI
|
|
+ |INTEN_WR_CPL|INTEN_RD_CPL|INTEN_SP_TIME_OUT|INTEN_ST_INIT_SP_CPL);
|
|
+
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void standard_crc_start(struct sunxi_owc *powc, bool crc_16, bool write)
|
|
+{
|
|
+ u32 reg_val = readl(powc->reg_base + OW_SMSC);
|
|
+
|
|
+ if (crc_16)
|
|
+ reg_val |= (1 << 2);
|
|
+ else
|
|
+ reg_val &= ~(1 << 2);
|
|
+
|
|
+ if (write)
|
|
+ reg_val |= (1 << 1);
|
|
+ else
|
|
+ reg_val |= (1 << 0);
|
|
+
|
|
+ writel(reg_val, powc->reg_base + OW_SMSC);
|
|
+}
|
|
+
|
|
+static void standard_crc_stop(struct sunxi_owc *powc, bool write)
|
|
+{
|
|
+ u32 reg_val = readl(powc->reg_base + OW_SMSC);
|
|
+
|
|
+ if (write)
|
|
+ reg_val &= ~(1 << 1);
|
|
+ else
|
|
+ reg_val &= ~(1 << 0);
|
|
+
|
|
+ writel(reg_val, powc->reg_base + OW_SMSC);
|
|
+}
|
|
+
|
|
+static int standard_crc_compare(struct sunxi_owc *powc)
|
|
+{
|
|
+ u8 timeout = 0;
|
|
+ u32 reg_val = readl(powc->reg_base + OW_SMSC);
|
|
+
|
|
+ reg_val |= (1 << 3);
|
|
+ writel(reg_val, powc->reg_base + OW_SMSC);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("write timeout\n");
|
|
+ return -1;
|
|
+ } else {
|
|
+ reg_val = readl(powc->reg_base + OW_SMSC);
|
|
+ if (reg_val & (1 << 5))
|
|
+ return -1;
|
|
+ else
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void standard_write(struct sunxi_owc *powc, u8 data)
|
|
+{
|
|
+ u8 timeout = 0;
|
|
+ /* direction: 1-write, 0-read*/
|
|
+ u32 reg_val = readl(powc->reg_base + OW_CTL);
|
|
+ reg_val |= (1 << 2);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ /* write data */
|
|
+ writel(data, powc->reg_base + OW_DATA);
|
|
+
|
|
+ /* start to send */
|
|
+ reg_val |= (1 << 4);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("write byte timeout\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void write_bit(struct sunxi_owc *powc, u8 data)
|
|
+{
|
|
+ u8 timeout = 0;
|
|
+ /* direction: 1-write, 0-read*/
|
|
+ u32 reg_val = readl(powc->reg_base + OW_CTL);
|
|
+ reg_val |= (1 << 2);
|
|
+ reg_val |= (1 << 5);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ /* write data */
|
|
+ data &= 0x01;
|
|
+ writel(data, powc->reg_base + OW_DATA);
|
|
+
|
|
+ /* start to send */
|
|
+ reg_val |= (1 << 4);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("write bit timeout\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static int read_bit(struct sunxi_owc *powc)
|
|
+{
|
|
+ u8 timeout = 0;
|
|
+ u8 data = 0;
|
|
+ /* direction: 1-write, 0-read*/
|
|
+ u32 reg_val = readl(powc->reg_base + OW_CTL);
|
|
+ reg_val &= ~(1 << 2);
|
|
+ reg_val |= (1 << 5);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ /* start to send */
|
|
+ reg_val |= (1 << 4);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("read bit timeout\n");
|
|
+ return -ENOMEM;
|
|
+ } else {
|
|
+ reg_val = readl(powc->reg_base + OW_DATA) & 0xFF;
|
|
+ data = (u8)reg_val;
|
|
+ }
|
|
+
|
|
+ return data;
|
|
+}
|
|
+
|
|
+static int standard_read(struct sunxi_owc *powc)
|
|
+{
|
|
+ u8 timeout = 0;
|
|
+ u8 data = 0;
|
|
+ /* direction: 1-write, 0-read*/
|
|
+ u32 reg_val = readl(powc->reg_base + OW_CTL);
|
|
+ reg_val &= ~(1 << 2);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ /* start to send */
|
|
+ reg_val |= (1 << 4);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("read byte timeout\n");
|
|
+ return -ENOMEM;
|
|
+ } else {
|
|
+ reg_val = readl(powc->reg_base + OW_DATA) & 0xFF;
|
|
+ data = (u8)reg_val;
|
|
+ }
|
|
+
|
|
+ return data;
|
|
+}
|
|
+
|
|
+static int sunxi_owc_initialization(struct sunxi_owc *powc)
|
|
+{
|
|
+ u32 reg_val = readl(powc->reg_base + OW_CTL);
|
|
+ u8 timeout = 0;
|
|
+
|
|
+ /* send initilization pulse */
|
|
+ reg_val |= (0x01 << 3);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+ reg_val |= (0x01 << 4);
|
|
+ writel(reg_val, powc->reg_base + OW_CTL);
|
|
+ timeout = wait_for_completion_timeout(&powc->done, usecs_to_jiffies(TIMEOUT));
|
|
+ if (powc->init_status) {
|
|
+ powc->init_status = 0;
|
|
+ }
|
|
+ if (timeout == 0) {
|
|
+ OWC_ERR("presence timeout\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* get all interrupt status */
|
|
+static u32 owc_get_interrupt_status(void __iomem *base_addr)
|
|
+{
|
|
+ return readl(base_addr + OW_INT_STATUS);
|
|
+}
|
|
+
|
|
+/* clear interrupt status */
|
|
+static void owc_clear_interrupt_status(void __iomem *base_addr, u32 bitmap)
|
|
+{
|
|
+ writel(bitmap, base_addr + OW_INT_STATUS);
|
|
+}
|
|
+
|
|
+static irqreturn_t sunxi_owc_interrupt(int irqmun, void *dev_id)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)dev_id;
|
|
+ u32 irq_status = 0;
|
|
+
|
|
+ irq_status = owc_get_interrupt_status(powc->reg_base);
|
|
+ owc_clear_interrupt_status(powc->reg_base, irq_status);
|
|
+ /* standrad initilization Pulse finish */
|
|
+ if (irq_status & INT_ST_INIT_SP_CPL) {
|
|
+ powc->init_status = 1;
|
|
+ complete(&powc->done);
|
|
+ }
|
|
+
|
|
+ /* HDQ time out */
|
|
+ if (irq_status & INT_SP_TIME_OUT) {
|
|
+ }
|
|
+
|
|
+ /* standard/HDQ read completed */
|
|
+ if (irq_status & INT_OW_RD_CPL) {
|
|
+ complete(&powc->done);
|
|
+ }
|
|
+
|
|
+ /* standard/HDQ write completed */
|
|
+ if (irq_status & INT_OW_WR_CPL) {
|
|
+ complete(&powc->done);
|
|
+ }
|
|
+
|
|
+ /* standard CRC completed */
|
|
+ if (irq_status & INT_ST_CRC_FINI) {
|
|
+ complete(&powc->done);
|
|
+ }
|
|
+
|
|
+ /* deglitch detected */
|
|
+ if (irq_status & INT_DGCH_OCCUR) {
|
|
+ powc->presence_status = 1;
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static uint32_t sunxi_owc_clk_init(struct sunxi_owc *powc)
|
|
+{
|
|
+ struct platform_device *pdev = powc->pdev;
|
|
+ struct device_node *node = pdev->dev.of_node;
|
|
+
|
|
+ if (NULL == pdev || !of_device_is_available(node)) {
|
|
+ OWC_ERR("platform_device invalid!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ powc->pclk = of_clk_get(node, 0);
|
|
+ if (!powc->pclk || IS_ERR(powc->pclk)) {
|
|
+ OWC_ERR("err: try to get pclk clock fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ powc->mclk = of_clk_get(node, 1);
|
|
+ if (!powc->mclk || IS_ERR(powc->mclk)) {
|
|
+ OWC_ERR("err: try to get mclk clock fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (clk_set_parent(powc->mclk, powc->pclk)) {
|
|
+ OWC_ERR("set mclk parent to pclk fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (of_property_read_u32(node, "clock-frequency", &powc->clk_freq)) {
|
|
+ OWC_INFO("get clock-frequency fail! use default 24Mhz\n");
|
|
+ powc->clk_freq = 24000000;
|
|
+ }
|
|
+
|
|
+ if (clk_set_rate(powc->mclk, powc->clk_freq)) {
|
|
+ OWC_ERR("set owc_clk freq failed!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (clk_prepare_enable(powc->mclk)) {
|
|
+ OWC_ERR("try to enable owc_clk failed!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uint32_t sunxi_owc_clk_exit(struct sunxi_owc *powc)
|
|
+{
|
|
+ if (NULL == powc->mclk || IS_ERR(powc->mclk)) {
|
|
+ OWC_ERR("owc_clk handle is invalid, just return!\n");
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ clk_disable_unprepare(powc->mclk);
|
|
+ clk_put(powc->mclk);
|
|
+ powc->mclk = NULL;
|
|
+ }
|
|
+ if (NULL == powc->pclk || IS_ERR(powc->pclk)) {
|
|
+ OWC_ERR("owc pclk handle is invalid, just return!\n");
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ clk_put(powc->pclk);
|
|
+ powc->pclk = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int owc_request_gpio(struct sunxi_owc *powc)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct pinctrl_state *pctrl_state = NULL;
|
|
+
|
|
+ powc->pctrl = devm_pinctrl_get(&(powc->pdev->dev));
|
|
+ if (IS_ERR_OR_NULL(powc->pctrl)) {
|
|
+ OWC_ERR("request pinctrl handle fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ pctrl_state = pinctrl_lookup_state(powc->pctrl, PINCTRL_STATE_DEFAULT);
|
|
+ if (IS_ERR(pctrl_state)) {
|
|
+ OWC_ERR("look_up_state fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = pinctrl_select_state(powc->pctrl, pctrl_state);
|
|
+ if (ret < 0) {
|
|
+ OWC_ERR("select_state fail!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void owc_release_gpio(struct sunxi_owc *powc)
|
|
+{
|
|
+ if (!IS_ERR_OR_NULL(powc->pctrl))
|
|
+ devm_pinctrl_put(powc->pctrl);
|
|
+ powc->pctrl = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+static u8 owc_read_bit(void *data)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)data;
|
|
+
|
|
+ return read_bit(powc);
|
|
+}
|
|
+
|
|
+static void owc_write_bit(void *data, u8 bit)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)data;
|
|
+
|
|
+ write_bit(powc, bit);
|
|
+}
|
|
+
|
|
+static u8 owc_read_byte(void *data)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)data;
|
|
+
|
|
+ return standard_read(powc);
|
|
+}
|
|
+
|
|
+static void owc_write_byte(void *data, u8 byte)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)data;
|
|
+
|
|
+ standard_write(powc, byte);
|
|
+}
|
|
+
|
|
+static u8 owc_reset_bus(void *data)
|
|
+{
|
|
+ struct sunxi_owc *powc = (struct sunxi_owc *)data;
|
|
+ int ret;
|
|
+
|
|
+ ret = sunxi_owc_initialization(powc);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct w1_bus_master sunxi_owc_master = {
|
|
+ .read_bit = owc_read_bit,
|
|
+ .write_bit = owc_write_bit,
|
|
+ .read_byte = owc_read_byte,
|
|
+ .write_byte = owc_write_byte,
|
|
+ .reset_bus = owc_reset_bus,
|
|
+};
|
|
+
|
|
+static int sunxi_owc_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device_node *node = pdev->dev.of_node;
|
|
+ struct resource *mem_res = NULL;
|
|
+ int ret = 0;
|
|
+
|
|
+ OWC_ENTER();
|
|
+
|
|
+ powc = kzalloc(sizeof(struct sunxi_owc), GFP_KERNEL);
|
|
+ if (!powc) {
|
|
+ OWC_ERR("kzalloc struct sunxi_owc fail!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ powc->pdev = pdev;
|
|
+
|
|
+ if (!of_device_is_available(node)) {
|
|
+ OWC_ERR("invalid node!\n");
|
|
+ ret = -EINVAL;
|
|
+ goto emloc;
|
|
+ }
|
|
+
|
|
+ if (sunxi_owc_clk_init(powc)) {
|
|
+ OWC_ERR("sunxi_owc_clk_init fail!\n");
|
|
+ ret = -EINVAL;
|
|
+ goto eclk;
|
|
+ }
|
|
+
|
|
+ powc->irq_num = platform_get_irq(pdev, 0);
|
|
+ if (powc->irq_num < 0) {
|
|
+ OWC_ERR("get irq number fail!\n");
|
|
+ ret = -EINVAL;
|
|
+ goto eclk;
|
|
+ }
|
|
+
|
|
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (mem_res == NULL) {
|
|
+ OWC_ERR("failed to get MEM res\n");
|
|
+ ret = -ENXIO;
|
|
+ goto eclk;
|
|
+ }
|
|
+
|
|
+ if (!request_mem_region(mem_res->start,
|
|
+ resource_size(mem_res), mem_res->name)) {
|
|
+ OWC_ERR("failed to request mem region\n");
|
|
+ ret = -EINVAL;
|
|
+ goto eclk;
|
|
+ }
|
|
+
|
|
+ powc->reg_base = ioremap(mem_res->start, resource_size(mem_res));
|
|
+ if (!powc->reg_base) {
|
|
+ OWC_ERR("failed to io remap\n");
|
|
+ ret = -EIO;
|
|
+ goto eiomem;
|
|
+ }
|
|
+ powc->mem_res = mem_res;
|
|
+
|
|
+ if (request_irq(powc->irq_num, sunxi_owc_interrupt,
|
|
+ IRQF_TRIGGER_NONE, "owc", powc)) {
|
|
+ OWC_ERR("request irq fail!\n");
|
|
+ ret = -EINVAL;
|
|
+ goto eiomap;
|
|
+ }
|
|
+
|
|
+ if (owc_request_gpio(powc)) {
|
|
+ OWC_ERR("failed to request gpio\n");
|
|
+ ret = -EINVAL;
|
|
+ goto eirq;
|
|
+ }
|
|
+
|
|
+
|
|
+ spin_lock_init(&powc->owc_lock);
|
|
+ init_completion(&powc->done);
|
|
+ sunxi_owc_setup(powc);
|
|
+
|
|
+ sunxi_owc_master.data = (void *)powc;
|
|
+ ret = w1_add_master_device(&sunxi_owc_master);
|
|
+ platform_set_drvdata(pdev, powc);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+
|
|
+eirq:
|
|
+ free_irq(powc->irq_num, powc);
|
|
+
|
|
+eiomap:
|
|
+ iounmap(powc->reg_base);
|
|
+
|
|
+eiomem:
|
|
+ release_mem_region(mem_res->start, resource_size(mem_res));
|
|
+
|
|
+eclk:
|
|
+ sunxi_owc_clk_exit(powc);
|
|
+
|
|
+emloc:
|
|
+ kfree(powc);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+static int sunxi_owc_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct sunxi_owc *powc = platform_get_drvdata(pdev);
|
|
+
|
|
+ w1_remove_master_device(&sunxi_owc_master);
|
|
+ free_irq(powc->irq_num, powc);
|
|
+ iounmap(powc->reg_base);
|
|
+ release_mem_region(powc->mem_res->start,
|
|
+ resource_size(powc->mem_res));
|
|
+ owc_release_gpio(powc);
|
|
+ sunxi_owc_clk_exit(powc);
|
|
+
|
|
+
|
|
+ OWC_EXIT();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int sunxi_owc_suspend(struct device *dev)
|
|
+{
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
+ struct sunxi_owc *powc = platform_get_drvdata(pdev);
|
|
+ struct pinctrl_state *pctrl_state = NULL;
|
|
+
|
|
+ powc->suspended = true;
|
|
+
|
|
+ if (sunxi_owc_clk_exit(powc)) {
|
|
+ OWC_ERR("OWC suspend failed !\n");
|
|
+ powc->suspended = false;
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (!IS_ERR_OR_NULL(powc->pctrl)) {
|
|
+ pctrl_state = pinctrl_lookup_state(powc->pctrl,
|
|
+ PINCTRL_STATE_SLEEP);
|
|
+ if (IS_ERR(pctrl_state)) {
|
|
+ OWC_ERR("OWC pinctrl lookup sleep fail\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (pinctrl_select_state(powc->pctrl, pctrl_state) < 0) {
|
|
+ OWC_ERR("OWC pinctrl select sleep fail\n");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ disable_irq_nosync(powc->irq_num);
|
|
+
|
|
+ OWC_DBG("OWC suspend okay\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sunxi_owc_resume(struct device *dev)
|
|
+{
|
|
+ struct platform_device *pdev = to_platform_device(dev);
|
|
+ struct sunxi_owc *powc = platform_get_drvdata(pdev);
|
|
+ struct pinctrl_state *pctrl_state = NULL;
|
|
+
|
|
+
|
|
+ powc->suspended = false;
|
|
+
|
|
+ if (sunxi_owc_clk_init(powc)) {
|
|
+ OWC_ERR("OWC resume failed !\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (!IS_ERR_OR_NULL(powc->pctrl)) {
|
|
+ pctrl_state = pinctrl_lookup_state(powc->pctrl,
|
|
+ PINCTRL_STATE_DEFAULT);
|
|
+ if (IS_ERR(pctrl_state)) {
|
|
+ OWC_ERR("OWC pinctrl lookup default fail\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (pinctrl_select_state(powc->pctrl, pctrl_state) < 0) {
|
|
+ OWC_ERR("OWC pinctrl select default fail\n");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ enable_irq(powc->irq_num);
|
|
+
|
|
+ OWC_DBG("OWC resume okay\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops sunxi_owc_dev_pm_ops = {
|
|
+ .suspend = sunxi_owc_suspend,
|
|
+ .resume = sunxi_owc_resume,
|
|
+};
|
|
+
|
|
+#define SUNXI_OWC_DEV_PM_OPS (&sunxi_owc_dev_pm_ops)
|
|
+#else
|
|
+#define SUNXI_OWC_DEV_PM_OPS NULL
|
|
+#endif
|
|
+
|
|
+static const struct of_device_id sunxi_owc_match[] = {
|
|
+ {.compatible = "allwinner,sunxi-owc",},
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, sunxi_owc_match);
|
|
+
|
|
+static struct platform_driver owc_platform_driver = {
|
|
+ .probe = sunxi_owc_probe,
|
|
+ .remove = sunxi_owc_remove,
|
|
+ .driver = {
|
|
+ .name = OWC_MODULE_NAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ .pm = SUNXI_OWC_DEV_PM_OPS,
|
|
+ .of_match_table = sunxi_owc_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init sunxi_owc_init(void)
|
|
+{
|
|
+ return platform_driver_register(&owc_platform_driver);
|
|
+}
|
|
+
|
|
+static void __exit sunxi_owc_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&owc_platform_driver);
|
|
+}
|
|
+
|
|
+module_init(sunxi_owc_init);
|
|
+module_exit(sunxi_owc_exit);
|
|
+MODULE_DESCRIPTION("One Wire Driver");
|
|
+MODULE_AUTHOR("lihuaxing");
|
|
+MODULE_LICENSE("GPL");
|