Loading Documentation/devicetree/bindings/i2c/qcom,i2c-qcom-geni.txt +5 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ Required properties: - #size-cells: Should be <0> as i2c addresses have no size component - qcom,wrapper-core: Wrapper QUPv3 core containing this I2C controller. Optional property: - qcom,clk-freq-out : Desired I2C bus clock frequency in Hz. When missing default to 400000Hz. Child nodes should conform to i2c bus binding. Example: Loading @@ -32,4 +36,5 @@ i2c@a94000 { #address-cells = <1>; #size-cells = <0>; qcom,wrapper-core = <&qupv3_0>; qcom,clk-freq-out = <400000>; }; drivers/i2c/busses/i2c-qcom-geni.c +63 −8 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ struct geni_i2c_dev { int cur_rd; struct device *wrapper_dev; void *ipcl; int clk_fld_idx; }; struct geni_i2c_err_log { Loading @@ -109,12 +110,52 @@ static struct geni_i2c_err_log gi2c_log[] = { [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"}, }; static inline void qcom_geni_i2c_conf(void __iomem *base, int dfs, int div) struct geni_i2c_clk_fld { u32 clk_freq_out; u8 clk_div; u8 t_high; u8 t_low; u8 t_cycle; }; static struct geni_i2c_clk_fld geni_i2c_clk_map[] = { {KHz(100), 7, 10, 11, 26}, {KHz(400), 2, 5, 12, 24}, {KHz(1000), 1, 3, 9, 18}, }; static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c) { geni_write_reg(dfs, base, SE_GENI_CLK_SEL); geni_write_reg((div << 4) | 1, base, GENI_SER_M_CLK_CFG); geni_write_reg(((5 << 20) | (0xC << 10) | 0x18), base, SE_I2C_SCL_COUNTERS); int i; int ret = 0; bool clk_map_present = false; struct geni_i2c_clk_fld *itr = geni_i2c_clk_map; for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) { if (itr->clk_freq_out == gi2c->i2c_rsc.clk_freq_out) { clk_map_present = true; break; } } if (clk_map_present) gi2c->clk_fld_idx = i; else ret = -EINVAL; return ret; } static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs) { struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx; geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL); geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG); geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) | itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS); /* * Ensure Clk config completes before return. */ Loading Loading @@ -283,7 +324,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, pm_runtime_set_suspended(gi2c->dev); return ret; } qcom_geni_i2c_conf(gi2c->base, 0, 2); qcom_geni_i2c_conf(gi2c, 0); dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n", num, msgs[0].len, msgs[0].flags); for (i = 0; i < num; i++) { Loading Loading @@ -485,12 +526,26 @@ static int geni_i2c_probe(struct platform_device *pdev) return ret; } if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out", &gi2c->i2c_rsc.clk_freq_out)) { dev_info(&pdev->dev, "Bus frequency not specified, default to 400KHz.\n"); gi2c->i2c_rsc.clk_freq_out = KHz(400); } gi2c->irq = platform_get_irq(pdev, 0); if (gi2c->irq < 0) { dev_err(gi2c->dev, "IRQ error for i2c-geni\n"); return gi2c->irq; } ret = geni_i2c_clk_map_idx(gi2c); if (ret) { dev_err(gi2c->dev, "Invalid clk frequency %d KHz: %d\n", gi2c->i2c_rsc.clk_freq_out, ret); return ret; } gi2c->adap.algo = &geni_i2c_algo; init_completion(&gi2c->xfer); platform_set_drvdata(pdev, gi2c); Loading include/linux/qcom-geni-se.h +3 −0 Original line number Diff line number Diff line Loading @@ -64,11 +64,14 @@ struct se_geni_rsc { struct pinctrl *geni_pinctrl; struct pinctrl_state *geni_gpio_active; struct pinctrl_state *geni_gpio_sleep; int clk_freq_out; }; #define PINCTRL_DEFAULT "default" #define PINCTRL_SLEEP "sleep" #define KHz(freq) (1000 * (freq)) /* Common SE registers */ #define GENI_INIT_CFG_REVISION (0x0) #define GENI_S_INIT_CFG_REVISION (0x4) Loading Loading
Documentation/devicetree/bindings/i2c/qcom,i2c-qcom-geni.txt +5 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ Required properties: - #size-cells: Should be <0> as i2c addresses have no size component - qcom,wrapper-core: Wrapper QUPv3 core containing this I2C controller. Optional property: - qcom,clk-freq-out : Desired I2C bus clock frequency in Hz. When missing default to 400000Hz. Child nodes should conform to i2c bus binding. Example: Loading @@ -32,4 +36,5 @@ i2c@a94000 { #address-cells = <1>; #size-cells = <0>; qcom,wrapper-core = <&qupv3_0>; qcom,clk-freq-out = <400000>; };
drivers/i2c/busses/i2c-qcom-geni.c +63 −8 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ struct geni_i2c_dev { int cur_rd; struct device *wrapper_dev; void *ipcl; int clk_fld_idx; }; struct geni_i2c_err_log { Loading @@ -109,12 +110,52 @@ static struct geni_i2c_err_log gi2c_log[] = { [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"}, }; static inline void qcom_geni_i2c_conf(void __iomem *base, int dfs, int div) struct geni_i2c_clk_fld { u32 clk_freq_out; u8 clk_div; u8 t_high; u8 t_low; u8 t_cycle; }; static struct geni_i2c_clk_fld geni_i2c_clk_map[] = { {KHz(100), 7, 10, 11, 26}, {KHz(400), 2, 5, 12, 24}, {KHz(1000), 1, 3, 9, 18}, }; static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c) { geni_write_reg(dfs, base, SE_GENI_CLK_SEL); geni_write_reg((div << 4) | 1, base, GENI_SER_M_CLK_CFG); geni_write_reg(((5 << 20) | (0xC << 10) | 0x18), base, SE_I2C_SCL_COUNTERS); int i; int ret = 0; bool clk_map_present = false; struct geni_i2c_clk_fld *itr = geni_i2c_clk_map; for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) { if (itr->clk_freq_out == gi2c->i2c_rsc.clk_freq_out) { clk_map_present = true; break; } } if (clk_map_present) gi2c->clk_fld_idx = i; else ret = -EINVAL; return ret; } static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs) { struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx; geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL); geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG); geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) | itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS); /* * Ensure Clk config completes before return. */ Loading Loading @@ -283,7 +324,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, pm_runtime_set_suspended(gi2c->dev); return ret; } qcom_geni_i2c_conf(gi2c->base, 0, 2); qcom_geni_i2c_conf(gi2c, 0); dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n", num, msgs[0].len, msgs[0].flags); for (i = 0; i < num; i++) { Loading Loading @@ -485,12 +526,26 @@ static int geni_i2c_probe(struct platform_device *pdev) return ret; } if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out", &gi2c->i2c_rsc.clk_freq_out)) { dev_info(&pdev->dev, "Bus frequency not specified, default to 400KHz.\n"); gi2c->i2c_rsc.clk_freq_out = KHz(400); } gi2c->irq = platform_get_irq(pdev, 0); if (gi2c->irq < 0) { dev_err(gi2c->dev, "IRQ error for i2c-geni\n"); return gi2c->irq; } ret = geni_i2c_clk_map_idx(gi2c); if (ret) { dev_err(gi2c->dev, "Invalid clk frequency %d KHz: %d\n", gi2c->i2c_rsc.clk_freq_out, ret); return ret; } gi2c->adap.algo = &geni_i2c_algo; init_completion(&gi2c->xfer); platform_set_drvdata(pdev, gi2c); Loading
include/linux/qcom-geni-se.h +3 −0 Original line number Diff line number Diff line Loading @@ -64,11 +64,14 @@ struct se_geni_rsc { struct pinctrl *geni_pinctrl; struct pinctrl_state *geni_gpio_active; struct pinctrl_state *geni_gpio_sleep; int clk_freq_out; }; #define PINCTRL_DEFAULT "default" #define PINCTRL_SLEEP "sleep" #define KHz(freq) (1000 * (freq)) /* Common SE registers */ #define GENI_INIT_CFG_REVISION (0x0) #define GENI_S_INIT_CFG_REVISION (0x4) Loading